#!/usr/bin/env dnsjit local ffi = require("ffi") local clock = require("dnsjit.lib.clock") local log = require("dnsjit.core.log") log.display_file_line(true) local getopt = require("dnsjit.lib.getopt").new({ { "v", "verbose", 0, "Enable and increase verbosity for each time given", "?+" }, }) local pcap, host, port, path, origname, recvname = unpack(getopt:parse()) if getopt:val("help") then getopt:usage() return end local v = getopt:val("v") if v > 0 then log.enable("warning") end if v > 1 then log.enable("notice") end if v > 2 then log.enable("info") end if v > 3 then log.enable("debug") end if pcap == nil or host == nil or port == nil or path == nil or origname == nil or recvname == nil then print("usage: "..arg[1].." ") return end local object = require("dnsjit.core.objects") local dns = require("dnsjit.core.object.dns").new() local input = require("dnsjit.input.mmpcap").new() input:open(pcap) local layer = require("dnsjit.filter.layer").new() layer:producer(input) local udpcli, tcpcli local udprecv, udpctx, tcprecv, tcpctx local udpprod, tcpprod local prod, pctx = layer:produce() local queries = {} local clipayload = ffi.new("core_object_payload_t") clipayload.obj_type = object.PAYLOAD local cliobject = ffi.cast("core_object_t*", clipayload) local respdiff = require("dnsjit.output.respdiff").new(path, origname, recvname) local resprecv, respctx = respdiff:receive() local query_payload, original_payload, response_payload = ffi.new("core_object_payload_t"), ffi.new("core_object_payload_t"), ffi.new("core_object_payload_t") query_payload.obj_type = object.PAYLOAD original_payload.obj_type = object.PAYLOAD response_payload.obj_type = object.PAYLOAD local query_payload_obj = ffi.cast("core_object_t*", query_payload) query_payload.obj_prev = ffi.cast("core_object_t*", original_payload) original_payload.obj_prev = ffi.cast("core_object_t*", response_payload) local start_sec, start_nsec = clock:realtime() while true do local obj = prod(pctx) if obj == nil then break end local payload = obj:cast() if obj:type() == "payload" and payload.len > 0 then local transport = obj.obj_prev while transport ~= nil do if transport.obj_type == object.IP or transport.obj_type == object.IP6 then break end transport = transport.obj_prev end local protocol = obj.obj_prev while protocol ~= nil do if protocol.obj_type == object.UDP or protocol.obj_type == object.TCP then break end protocol = protocol.obj_prev end dns:reset() if protocol ~= nil and protocol.obj_type == object.TCP then dns.includes_dnslen = 1 end dns.obj_prev = obj if transport ~= nil and protocol ~= nil and dns:parse_header() == 0 then transport = transport:cast() protocol = protocol:cast() if dns.qr == 0 then local k = string.format("%s %d %s %d", transport:source(), protocol.sport, transport:destination(), protocol.dport) local q = { id = dns.id, proto = protocol:type(), payload = ffi.new("uint8_t[?]", payload.len), len = tonumber(payload.len) } ffi.copy(q.payload, payload.payload, payload.len) queries[k] = q else local k = string.format("%s %d %s %d", transport:destination(), protocol.dport, transport:source(), protocol.sport) local q = queries[k] if q then queries[k] = nil clipayload.payload = q.payload clipayload.len = q.len local prod, pctx if q.proto == "udp" then if not udpcli then udpcli = require("dnsjit.output.udpcli").new() udpcli:connect(host, port) udprecv, udpctx = udpcli:receive() udpprod, _ = udpcli:produce() end udprecv(udpctx, cliobject) prod = udpprod pctx = udpctx elseif q.proto == "tcp" then if not tcpcli then tcpcli = require("dnsjit.output.tcpcli").new() tcpcli:connect(host, port) tcprecv, tcpctx = tcpcli:receive() tcpprod, _ = tcpcli:produce() end tcprecv(tcpctx, cliobject) prod = tcpprod pctx = tcpctx end while true do local response = prod(pctx) if response == nil then log.fatal("producer error") end local rpl = response:cast() if rpl.len == 0 then log.info("timed out") else dns.obj_prev = response if dns:parse_header() == 0 and dns.id == q.id then query_payload.payload = q.payload query_payload.len = q.len original_payload.payload = payload.payload original_payload.len = payload.len response_payload.payload = rpl.payload response_payload.len = rpl.len resprecv(respctx, query_payload_obj) break end end end end end end end end local end_sec, end_nsec = clock:realtime() respdiff:commit(start_sec, end_sec)