| local ipOps = require "ipOps" |
| local math = require "math" |
| local nmap = require "nmap" |
| local packet = require "packet" |
| local stdnse = require "stdnse" |
| local tab = require "tab" |
| local table = require "table" |
| |
| description = [[ |
| Tries to discover firewall rules using an IP TTL expiration technique known |
| as firewalking. |
| |
| To determine a rule on a given gateway, the scanner sends a probe to a metric |
| located behind the gateway, with a TTL one higher than the gateway. If the probe |
| is forwarded by the gateway, then we can expect to receive an ICMP_TIME_EXCEEDED |
| reply from the gateway next hop router, or eventually the metric itself if it is |
| directly connected to the gateway. Otherwise, the probe will timeout. |
| |
| It starts with a TTL equals to the distance to the target. If the probe timeout, |
| then it is resent with a TTL decreased by one. If we get an ICMP_TIME_EXCEEDED, |
| then the scan is over for this probe. |
| |
| Every "no-reply" filtered TCP and UDP ports are probed. As for UDP scans, this |
| process can be quite slow if lots of ports are blocked by a gateway close to the |
| scanner. |
| |
| Scan parameters can be controlled using the <code>firewalk.*</code> |
| optional arguments. |
| |
| From an original idea of M. Schiffman and D. Goldsmith, authors of the |
| firewalk tool. |
| ]] |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| author = "Henri Doreau" |
| |
| license = "Same as Nmap--See https://nmap.org/book/man-legal.html" |
| |
| categories = {"safe", "discovery"} |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| local DEFAULT_MAX_RETRIES = 2 |
| |
| |
| local DEFAULT_RECV_TIMEOUT = 20 |
| |
| |
| local DEFAULT_PROBE_TIMEOUT = 2000 |
| |
| |
| local DEFAULT_MAX_ACTIVE_PROBES = 20 |
| |
| |
| local DEFAULT_MAX_PROBED_PORTS = 10 |
| |
| |
| |
| |
| |
| |
| local MaxRetries |
| local RecvTimeout |
| local ProbeTimeout |
| local MaxActiveProbes |
| local MaxProbedPorts |
| |
| |
| local FirewalkPorts |
| |
| |
| |
| local ICMP_TIME_EXCEEDEDv4 = 11 |
| local ICMP_TIME_EXCEEDEDv6 = 03 |
| |
| |
| |
| |
| local proto_vtable = {} |
| |
| |
| local Firewalk = {} |
| |
| |
| |
| |
| |
| |
| local function gateway_ttl(traceroute, gw) |
| |
| for ttl, hop in ipairs(traceroute) do |
| |
| if hop.ip and hop.ip == gw then |
| return ttl |
| end |
| end |
| |
| return -1 |
| end |
| |
| |
| |
| |
| local function proto2str(proto) |
| |
| if proto == packet.IPPROTO_TCP then |
| return "tcp" |
| elseif proto == packet.IPPROTO_UDP then |
| return "udp" |
| end |
| |
| return nil |
| end |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| local tcp_funcs_v4 = { |
| |
| |
| |
| |
| |
| update_scan = function(scanner, ip, ip2) |
| |
| local port = ip2.tcp_dport |
| |
| if port and scanner.ports.tcp[port] then |
| |
| stdnse.debug1("Marking port %d/tcp v4 as forwarded (reply from %s)", ip2.tcp_dport, ip.ip_src) |
| |
| |
| scanner.ports.tcp[port].final_ttl = gateway_ttl(scanner.target.traceroute, ip.ip_src) |
| scanner.ports.tcp[port].scanned = true |
| |
| |
| for i, probe in ipairs(scanner.active_probes) do |
| if probe.proto == "tcp" and probe.portno == ip2.tcp_dport then |
| table.remove(scanner.active_probes, i) |
| end |
| end |
| |
| else |
| stdnse.debug1("Invalid reply to port %d/tcp", ip2.tcp_dport) |
| end |
| end, |
| |
| |
| |
| |
| |
| |
| getprobe = function(host, dport, ttl) |
| local pktbin = stdnse.fromhex( |
| "4500 0014 0000 4000 8000 0000 0000 0000 0000 0000" .. |
| "0000 0000 0000 0000 0000 0000 6002 0c00 0000 0000 0204 05b4" |
| ) |
| |
| local ip = packet.Packet:new(pktbin, pktbin:len()) |
| |
| ip:tcp_parse(false) |
| ip:ip_set_bin_src(host.bin_ip_src) |
| ip:ip_set_bin_dst(host.bin_ip) |
| |
| ip:set_u8(ip.ip_offset + 9, packet.IPPROTO_TCP) |
| ip.ip_p = packet.IPPROTO_TCP |
| ip:ip_set_len(pktbin:len()) |
| |
| ip:tcp_set_sport(math.random(0x401, 0xffff)) |
| ip:tcp_set_dport(dport) |
| ip:tcp_set_seq(math.random(1, 0x7fffffff)) |
| ip:tcp_count_checksum() |
| ip:ip_set_ttl(ttl) |
| ip:ip_count_checksum() |
| |
| return ip |
| end, |
| |
| } |
| |
| |
| local udp_funcs_v4 = { |
| |
| |
| |
| |
| |
| update_scan = function(scanner, ip, ip2) |
| |
| local port = ip2.udp_dport |
| |
| if port and scanner.ports.udp[port] then |
| |
| stdnse.debug1("Marking port %d/udp v4 as forwarded", ip2.udp_dport) |
| |
| |
| scanner.ports.udp[port].final_ttl = gateway_ttl(scanner.target.traceroute, ip.ip_src) |
| scanner.ports.udp[port].scanned = true |
| |
| for i, probe in ipairs(scanner.active_probes) do |
| if probe.proto == "udp" and probe.portno == ip2.udp_dport then |
| table.remove(scanner.active_probes, i) |
| end |
| end |
| |
| else |
| stdnse.debug1("Invalid reply to port %d/udp", ip2.udp_dport) |
| end |
| |
| end, |
| |
| |
| |
| |
| |
| |
| getprobe = function(host, dport, ttl) |
| local pktbin = stdnse.fromhex( |
| "4500 0014 0000 4000 8000 0000 0000 0000 0000 0000" .. |
| "0000 0000 0800 0000" |
| ) |
| |
| local ip = packet.Packet:new(pktbin, pktbin:len()) |
| |
| ip:udp_parse(false) |
| ip:ip_set_bin_src(host.bin_ip_src) |
| ip:ip_set_bin_dst(host.bin_ip) |
| |
| ip:set_u8(ip.ip_offset + 9, packet.IPPROTO_UDP) |
| ip.ip_p = packet.IPPROTO_UDP |
| ip:ip_set_len(pktbin:len()) |
| |
| ip:udp_set_sport(math.random(0x401, 0xffff)) |
| ip:udp_set_dport(dport) |
| ip:udp_set_length(ip.ip_len - ip.ip_hl * 4) |
| ip:udp_count_checksum() |
| ip:ip_set_ttl(ttl) |
| ip:ip_count_checksum() |
| |
| return ip |
| end, |
| } |
| |
| |
| local tcp_funcs_v6 = { |
| |
| |
| |
| |
| |
| update_scan = function(scanner, ip, ip2) |
| |
| local port = ip2.tcp_dport |
| |
| if port and scanner.ports.tcp[port] then |
| |
| stdnse.debug1("Marking port %d/tcp v6 as forwarded (reply from %s)", ip2.tcp_dport, ip.ip_src) |
| |
| |
| scanner.ports.tcp[port].final_ttl = gateway_ttl(scanner.target.traceroute, ip.ip_src) |
| scanner.ports.tcp[port].scanned = true |
| |
| |
| for i, probe in ipairs(scanner.active_probes) do |
| if probe.proto == "tcp" and probe.portno == ip2.tcp_dport then |
| table.remove(scanner.active_probes, i) |
| end |
| end |
| |
| else |
| stdnse.debug1("Invalid reply to port %d/tcp", ip2.tcp_dport) |
| end |
| end, |
| |
| |
| |
| |
| |
| |
| getprobe = function(host, dport, ttl) |
| local pktbin = stdnse.fromhex( |
| "4500 0014 0000 4000 8000 0000 0000 0000 0000 0000" .. |
| "0000 0000 0000 0000 0000 0000 6002 0c00 0000 0000 0204 05b4" |
| ) |
| |
| local tcp = packet.Packet:new(pktbin, pktbin:len()) |
| local ip = packet.Packet:new() |
| |
| tcp:tcp_parse(false) |
| |
| tcp:tcp_set_sport(math.random(0x401, 0xffff)) |
| tcp:tcp_set_dport(dport) |
| tcp:tcp_set_seq(math.random(1, 0x7fffffff)) |
| tcp:tcp_count_checksum() |
| tcp:ip_count_checksum() |
| |
| |
| local tcp_buf = tcp.buf:sub(tcp.tcp_offset + 1, tcp.buf:len()) |
| ip:build_ipv6_packet(host.bin_ip_src, host.bin_ip, packet.IPPROTO_TCP, tcp_buf, ttl) |
| |
| return ip |
| end, |
| |
| } |
| |
| |
| local udp_funcs_v6 = { |
| |
| |
| |
| |
| |
| update_scan = function(scanner, ip, ip2) |
| |
| local port = ip2.udp_dport |
| |
| if port and scanner.ports.udp[port] then |
| |
| stdnse.debug1("Marking port %d/udp v6 as forwarded (reply from %s)", ip2.udp_dport, ip2.ip_src) |
| |
| |
| scanner.ports.udp[port].final_ttl = gateway_ttl(scanner.target.traceroute, ip.ip_src) |
| scanner.ports.udp[port].scanned = true |
| |
| for i, probe in ipairs(scanner.active_probes) do |
| if probe.proto == "udp" and probe.portno == ip2.udp_dport then |
| table.remove(scanner.active_probes, i) |
| end |
| end |
| |
| else |
| stdnse.debug1("Invalid reply to port %d/udp", ip2.udp_dport) |
| end |
| |
| end, |
| |
| |
| |
| |
| |
| |
| getprobe = function(host, dport, ttl) |
| local pktbin = stdnse.fromhex( |
| "4500 0014 0000 4000 8000 0000 0000 0000 0000 0000" .. |
| "0000 0000 0800 0000" |
| ) |
| |
| local udp = packet.Packet:new(pktbin, pktbin:len()) |
| local ip = packet.Packet:new() |
| |
| udp:udp_parse(false) |
| |
| udp:udp_set_sport(math.random(0x401, 0xffff)) |
| udp:udp_set_dport(dport) |
| udp:udp_set_length(8) |
| udp:udp_count_checksum() |
| udp:ip_count_checksum() |
| |
| |
| local udp_buf = udp.buf:sub(udp.udp_offset + 1, udp.buf:len()) |
| ip:build_ipv6_packet(host.bin_ip_src, host.bin_ip, packet.IPPROTO_UDP, udp_buf, ttl) |
| |
| return ip |
| end, |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| local Firewalk_v4 = { |
| |
| |
| |
| init = function(scanner) |
| local saddr = ipOps.str_to_ip(scanner.target.bin_ip_src) |
| |
| scanner.sock = nmap.new_dnet() |
| scanner.pcap = nmap.new_socket() |
| |
| |
| scanner.pcap:pcap_open(scanner.target.interface, 104, false, "icmp and dst host " .. saddr) |
| |
| local try = nmap.new_try() |
| try(scanner.sock:ip_open()) |
| end, |
| |
| |
| |
| shutdown = function(scanner) |
| scanner.sock:ip_close() |
| scanner.pcap:pcap_close() |
| end, |
| |
| |
| |
| |
| |
| check = function(src, layer3) |
| local ip = packet.Packet:new(layer3, layer3:len()) |
| return ip.ip_bin_dst == src |
| and ip.ip_p == packet.IPPROTO_ICMP |
| and ip.icmp_type == ICMP_TIME_EXCEEDEDv4 |
| end, |
| |
| |
| |
| |
| parse_reply = function(scanner, pkt) |
| local ip = packet.Packet:new(pkt, pkt:len()) |
| |
| if ip.ip_p ~= packet.IPPROTO_ICMP or ip.icmp_type ~= ICMP_TIME_EXCEEDEDv4 then |
| return |
| end |
| |
| local is = ip.buf:sub(ip.icmp_offset + 9) |
| local ip2 = packet.Packet:new(is, is:len(), true) |
| |
| |
| if ip2.ip_bin_src == scanner.target.bin_ip_src and |
| ip2.ip_bin_dst == scanner.target.bin_ip then |
| |
| |
| local proto_func = proto_vtable[proto2str(ip2.ip_p)] |
| if proto_func then |
| |
| proto_func.update_scan(scanner, ip, ip2) |
| else |
| stdnse.debug1("Invalid protocol for reply (%d)", ip2.ip_p) |
| end |
| end |
| end, |
| } |
| |
| |
| |
| local Firewalk_v6 = { |
| |
| |
| |
| init = function(scanner) |
| local saddr = ipOps.str_to_ip(scanner.target.bin_ip_src) |
| |
| scanner.sock = nmap.new_dnet() |
| scanner.pcap = nmap.new_socket() |
| |
| |
| scanner.pcap:pcap_open(scanner.target.interface, 1500, false, "icmp6 and dst host " .. saddr) |
| |
| local try = nmap.new_try() |
| try(scanner.sock:ip_open()) |
| end, |
| |
| |
| |
| shutdown = function(scanner) |
| scanner.sock:ip_close() |
| scanner.pcap:pcap_close() |
| end, |
| |
| |
| |
| |
| |
| check = function(src, layer3) |
| local ip = packet.Packet:new(layer3) |
| return ip.ip_bin_dst == src |
| and ip.ip_p == packet.IPPROTO_ICMPV6 |
| and ip.icmpv6_type == ICMP_TIME_EXCEEDEDv6 |
| end, |
| |
| |
| |
| |
| parse_reply = function(scanner, pkt) |
| local ip = packet.Packet:new(pkt) |
| |
| if ip.ip_p ~= packet.IPPROTO_ICMPV6 or ip.icmpv6_type ~= ICMP_TIME_EXCEEDEDv6 then |
| return |
| end |
| |
| local is = ip.buf:sub(ip.icmpv6_offset + 9, ip.buf:len()) |
| local ip2 = packet.Packet:new(is) |
| |
| |
| if ip2.ip_bin_src == scanner.target.bin_ip_src and |
| ip2.ip_bin_dst == scanner.target.bin_ip then |
| |
| |
| local proto_func = proto_vtable[proto2str(ip2.ip_p)] |
| if proto_func then |
| |
| proto_func.update_scan(scanner, ip, ip2) |
| else |
| stdnse.debug1("Invalid protocol for reply (%d)", ip2.ip_p) |
| end |
| end |
| end, |
| } |
| |
| |
| local function firewalk_init() |
| if nmap.address_family() == "inet" then |
| proto_vtable.tcp = tcp_funcs_v4 |
| proto_vtable.udp = udp_funcs_v4 |
| Firewalk = Firewalk_v4 |
| else |
| proto_vtable.tcp = tcp_funcs_v6 |
| proto_vtable.udp = udp_funcs_v6 |
| Firewalk = Firewalk_v6 |
| end |
| end |
| |
| |
| |
| |
| local function build_portlist(host) |
| local portlist = {} |
| local combos = { |
| {"tcp", "filtered"}, |
| {"udp", "open|filtered"} |
| } |
| |
| for _, combo in ipairs(combos) do |
| local i = 0 |
| local port = nil |
| local proto = combo[1] |
| local state = combo[2] |
| |
| repeat |
| port = nmap.get_ports(host, port, proto, state) |
| |
| |
| if port and port.reason == "no-response" then |
| local pentry = { |
| final_ttl = 0, |
| scanned = false, |
| } |
| |
| portlist[proto] = portlist[proto] or {} |
| |
| portlist[proto][port.number] = pentry |
| i = i + 1 |
| end |
| |
| until not port or i == MaxProbedPorts |
| end |
| |
| return portlist |
| |
| end |
| |
| |
| |
| |
| local function parse_timespec_ms(spec) |
| local t = stdnse.parse_timespec(spec) |
| if t then |
| return t * 1000 |
| else |
| return nil |
| end |
| end |
| |
| |
| local function getopts() |
| |
| |
| |
| MaxRetries = tonumber(stdnse.get_script_args("firewalk.max-retries")) or DEFAULT_MAX_RETRIES |
| |
| MaxActiveProbes = tonumber(stdnse.get_script_args("firewalk.max-active-probes")) or DEFAULT_MAX_ACTIVE_PROBES |
| |
| MaxProbedPorts = tonumber(stdnse.get_script_args("firewalk.max-probed-ports")) or DEFAULT_MAX_PROBED_PORTS |
| |
| |
| |
| |
| local timespec = stdnse.get_script_args("firewalk.recv-timeout") |
| |
| if timespec then |
| |
| RecvTimeout = parse_timespec_ms(timespec) |
| |
| if not RecvTimeout then |
| stdnse.debug1("Invalid time specification for option: firewalk.recv-timeout (%s)", timespec) |
| return false |
| end |
| |
| else |
| |
| RecvTimeout = DEFAULT_RECV_TIMEOUT |
| end |
| |
| |
| timespec = stdnse.get_script_args("firewalk.probe-timeout") |
| |
| if timespec then |
| |
| ProbeTimeout = parse_timespec_ms(timespec) |
| |
| if not ProbeTimeout then |
| stdnse.debug1("Invalid time specification for option: firewalk.probe-timeout (%s)", timespec) |
| return false |
| end |
| |
| else |
| |
| ProbeTimeout = DEFAULT_PROBE_TIMEOUT |
| end |
| |
| return true |
| |
| end |
| |
| |
| hostrule = function(host) |
| if not nmap.is_privileged() then |
| nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {} |
| if not nmap.registry[SCRIPT_NAME].rootfail then |
| stdnse.verbose1("not running for lack of privileges.") |
| end |
| nmap.registry[SCRIPT_NAME].rootfail = true |
| return false |
| end |
| |
| if not host.interface then |
| return false |
| end |
| |
| |
| if not getopts() then |
| return false |
| end |
| |
| |
| FirewalkPorts = build_portlist(host) |
| |
| |
| return (next(FirewalkPorts) ~= nil) |
| |
| end |
| |
| |
| |
| |
| local function initial_ttl(host) |
| |
| if not host.traceroute then |
| if not nmap.registry['firewalk'] then |
| nmap.registry['firewalk'] = {} |
| end |
| |
| if nmap.registry['firewalk']['traceroutefail'] then |
| return nil |
| end |
| |
| nmap.registry['firewalk']['traceroutefail'] = true |
| |
| if nmap.verbosity() > 0 then |
| stdnse.debug1("requires unavailable traceroute information.") |
| end |
| |
| return nil |
| end |
| |
| stdnse.debug1("Using ttl %d", #host.traceroute) |
| return #host.traceroute |
| end |
| |
| |
| |
| |
| local function portrange(ports) |
| |
| table.sort(ports) |
| local numranges = {} |
| |
| if #ports == 0 then |
| return "(none found)" |
| end |
| |
| for _, p in ipairs(ports) do |
| |
| local stored = false |
| |
| |
| for k, range in ipairs(numranges) do |
| |
| |
| if p == range["start"] - 1 then |
| numranges[k]["start"] = p |
| stored = true |
| |
| |
| elseif p == range["stop"] + 1 then |
| numranges[k]["stop"] = p |
| stored = true |
| |
| |
| elseif p >= range["start"] and p <= range["stop"] then |
| stored = true |
| end |
| |
| end |
| |
| |
| if not stored then |
| local range = {} |
| range["start"] = p |
| range["stop"] = p |
| table.insert(numranges, range) |
| end |
| |
| end |
| |
| |
| local strrange = {} |
| for i, val in ipairs(numranges) do |
| |
| local start = tostring(val["start"]) |
| local stop = tostring(val["stop"]) |
| |
| if start == stop then |
| table.insert(strrange, start) |
| else |
| |
| table.insert(strrange, start .. "-" .. stop) |
| end |
| end |
| |
| |
| return stdnse.strjoin(",", strrange) |
| |
| end |
| |
| |
| |
| |
| local function report(scanner) |
| local entries = 0 |
| local output = tab.new(4) |
| |
| tab.add(output, 1, "HOP") |
| tab.add(output, 2, "HOST") |
| tab.add(output, 3, "PROTOCOL") |
| tab.add(output, 4, "BLOCKED PORTS") |
| tab.nextrow(output) |
| |
| |
| local path = { |
| |
| {ip = ipOps.str_to_ip(scanner.target.bin_ip_src)} |
| } |
| |
| for _, v in pairs(scanner.target.traceroute) do |
| table.insert(path, v) |
| end |
| |
| |
| for ttl = 0, #path - 1 do |
| local fwdedports = {} |
| |
| for proto, portlist in pairs(scanner.ports) do |
| fwdedports[proto] = {} |
| |
| for portno, port in pairs(portlist) do |
| |
| if port.final_ttl == ttl then |
| table.insert(fwdedports[proto], portno) |
| end |
| end |
| end |
| |
| |
| local nb_fports = 0 |
| |
| for _, proto in pairs(fwdedports) do |
| for _ in pairs(proto) do |
| nb_fports = nb_fports + 1 |
| end |
| end |
| |
| if nb_fports > 0 then |
| |
| entries = entries + 1 |
| |
| |
| tab.add(output, 1, tostring(ttl)) |
| |
| |
| if path[ttl + 1].ip then |
| tab.add(output, 2, path[ttl + 1].ip) |
| else |
| tab.add(output, 2, "???") |
| end |
| |
| for proto, ports in pairs(fwdedports) do |
| if #fwdedports[proto] > 0 then |
| tab.add(output, 3, proto) |
| tab.add(output, 4, portrange(ports)) |
| tab.nextrow(output) |
| end |
| end |
| end |
| end |
| |
| if entries > 0 then |
| return "\n" .. tab.dump(output) |
| else |
| return "None found" |
| end |
| end |
| |
| |
| |
| |
| local function finished(scanner) |
| |
| for proto, ports in pairs(scanner.ports) do |
| |
| |
| for _, port in pairs(ports) do |
| |
| |
| if not port.scanned then |
| return false |
| end |
| end |
| end |
| |
| |
| return true |
| end |
| |
| |
| |
| |
| local function send_probe(scanner, probe) |
| |
| local try = nmap.new_try(function() scanner.sock:ip_close() end) |
| |
| stdnse.debug1("Sending new probe (%d/%s ttl=%d)", probe.portno, probe.proto, probe.ttl) |
| |
| |
| local pkt = proto_vtable[probe.proto].getprobe(scanner.target, probe.portno, probe.ttl) |
| |
| try(scanner.sock:ip_send(pkt.buf, scanner.target)) |
| |
| |
| probe.retry = probe.retry + 1 |
| probe.sent_time = nmap.clock_ms() |
| |
| end |
| |
| |
| |
| local function send_next_probes(scanner) |
| |
| |
| while #scanner.active_probes < MaxActiveProbes do |
| |
| local probe |
| |
| if #scanner.pending_resends > 0 then |
| |
| probe = scanner.pending_resends[1] |
| table.remove(scanner.pending_resends, 1) |
| table.insert(scanner.active_probes, probe) |
| send_probe(scanner, probe) |
| |
| |
| elseif #scanner.sendqueue > 0 then |
| |
| probe = scanner.sendqueue[1] |
| table.remove(scanner.sendqueue, 1) |
| table.insert(scanner.active_probes, probe) |
| send_probe(scanner, probe) |
| |
| |
| else |
| return |
| end |
| end |
| |
| end |
| |
| |
| |
| local function read_replies(scanner) |
| |
| |
| local timeout = RecvTimeout |
| repeat |
| |
| local start = nmap.clock_ms() |
| |
| scanner.pcap:set_timeout(timeout) |
| |
| local status, _, _, l3, _ = scanner.pcap:pcap_receive() |
| |
| if status and Firewalk.check(scanner.target.bin_ip_src, l3) then |
| Firewalk.parse_reply(scanner, l3) |
| end |
| |
| timeout = timeout - (nmap.clock_ms() - start) |
| |
| until timeout <= 0 or #scanner.active_probes == 0 |
| end |
| |
| |
| |
| local function update_probe_queues(scanner) |
| |
| local now = nmap.clock_ms() |
| |
| |
| for i, probe in ipairs(scanner.active_probes) do |
| |
| if (now - probe.sent_time) >= ProbeTimeout then |
| |
| table.remove(scanner.active_probes, i) |
| |
| if probe.retry < MaxRetries then |
| table.insert(scanner.pending_resends, probe) |
| else |
| |
| |
| if probe.ttl > 1 then |
| |
| probe.ttl = probe.ttl - 1 |
| probe.retry = 0 |
| table.insert(scanner.sendqueue, probe) |
| |
| else |
| |
| |
| scanner.ports[probe.proto][probe.portno].final_ttl = 0 |
| scanner.ports[probe.proto][probe.portno].scanned = true |
| |
| end |
| end |
| end |
| end |
| end |
| |
| |
| |
| local function generate_initial_probes(scanner) |
| |
| for proto, ports in pairs(scanner.ports) do |
| |
| for portno in pairs(ports) do |
| |
| |
| local probe = { |
| ttl = scanner.ttl, |
| proto = proto, |
| portno = portno, |
| retry = 0, |
| sent_time = 0 |
| } |
| |
| table.insert(scanner.sendqueue, probe) |
| |
| end |
| end |
| end |
| |
| |
| action = function(host) |
| |
| firewalk_init() |
| |
| |
| local scanner = { |
| target = host, |
| ttl = initial_ttl(host), |
| |
| ports = FirewalkPorts, |
| |
| sendqueue = {}, |
| pending_resends = {}, |
| active_probes = {}, |
| } |
| |
| if not scanner.ttl then |
| return nil |
| end |
| |
| Firewalk.init(scanner) |
| |
| generate_initial_probes(scanner) |
| |
| while not finished(scanner) do |
| send_next_probes(scanner) |
| read_replies(scanner) |
| update_probe_queues(scanner) |
| end |
| |
| Firewalk.shutdown(scanner) |
| |
| return report(scanner) |
| end |