diff --git a/CHANGELOG b/CHANGELOG index 9c477ed2b..ad901e4af 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -28,6 +28,10 @@ o Fixed the nmap_command_path bug in Zenmap. The variable now actually by a new class in UmitConf called PathsConfig, which represents the [paths] section in zenmap.conf. [Jurand Nogiec] +o Added a new NSE Comm library for common network discovery tasks such + as banner-grabbing (get_banner()) and making a quick exchange of data + (exchange()). 16 scripts were updated to use this library. [Kris] + o Fixed a bug which caused -PN to erronously bail out for unprivileged users. Thanks to Jabra (jabra(a)spl0it.org) for the report. [Kris] diff --git a/docs/scripting.xml b/docs/scripting.xml index 79a991fdf..29bcea62d 100644 --- a/docs/scripting.xml +++ b/docs/scripting.xml @@ -1441,6 +1441,61 @@ if(s) code_to_be_done_on_match end + + + Common Communication Functions + + The comm module provides functions for common network discovery + tasks such as banner-grabbing and making a quick exchange of data. These functions' + return values are setup for use with exception handling via nmap.new_try(). + + + + These functions can all be passed a table of options, but it's not required. + The relevant indexes for this table are bytes, lines, + proto and timeout. bytes + is used to provide the minimum number of bytes required for a read. lines + does the same, but for the minimum number of lines. proto is used + to set the protocol to communicate with, defaulting to "tcp" if not provided. + timeout is used to set the socket timeout (see the socket function + set_timeout() for details). + + + + + + get_banner + + + This function simply connects to the specified port number on + the specified host and returns any data received. + bool is a boolean value indicating success. + If bool is true, then the second returned + value is the response from the target host. If bool + is false, an error message is returned as the second value instead + of a response. + + + + + + + exchange + + + This function connects to the specified port number on the + specified host, sends data, then waits for + and returns the response, if any. bool is a + boolean value indicating success. If bool is + true, then the second returned value is the response from the + target host. If bool is false, an error message + is returned as the second value instead of a response. + + + + + + Data File Parsing Functions diff --git a/nselib/comm.lua b/nselib/comm.lua new file mode 100644 index 000000000..48ad74cfb --- /dev/null +++ b/nselib/comm.lua @@ -0,0 +1,151 @@ +-- Kris Katterjohn 04/2008 + +module(..., package.seeall) + +------ +-- +-- The Functions: +-- +-- get_banner(host, port, [opts]) +-- exchange(host, port, data, [opts]) +-- +-- get_banner() does just what it sounds like it does: connects to the +-- host, reads whatever it gives us, and then returns it. +-- +-- exchange() connects to the host, sends the requested data, reads +-- whatever it gives us, and then returns it. +-- +-- Both of these functions return multiple values so that they can be +-- used with exception handling via nmap.new_try(). The second value +-- they return is either the response from the host, or the error message +-- from one of the previous calls (connect, send, receive*). +-- +-- These functions can be passed a table of options with the following keys: +-- +-- bytes: Specifies the minimum amount of bytes are to be read from the host +-- lines: Specifies the minimum amount of lines are to be read from the host +-- proto: Specifies the protocol to be used with the connect() call +-- timeout: Sets the socket's timeout with nmap.set_timeout() +-- +-- If neither lines nor bytes are specified, the calls read as many lines +-- as possible. If only bytes if specified, then it only tries to read that +-- many bytes. Likewise, it only lines if specified, then it only tries to +-- read that many lines. If they're both specified, the lines value is used. +-- +------ + +-- Makes sure that opts exists and the default proto is there +local initopts = function(opts) + if not opts then + opts = {} + end + + if not opts.proto then + opts.proto = "tcp" + end + + return opts +end + +-- Sets up the socket and connects to host:port +local setup_connect = function(host, port, opts) + if type(host) ~= "table" then + host = {ip = host} + end + + local target = host.targetname or host.ip or host.name + + if type(port) ~= "table" then + port = {number = port} + end + + local sock = nmap.new_socket() + + if opts.timeout then + sock:set_timeout(opts.timeout) + end + + local status, err = sock:connect(target, port.number, opts.proto) + + if not status then + return status, err + end + + return true, sock +end + +local read = function(sock, opts) + local line, response, status + + if opts.lines then + status, response = sock:receive_lines(opts.lines) + return status, response + elseif opts.bytes then + status, response = sock:receive_bytes(opts.bytes) + return status, response + end + + response = "" + + while true do + status, line = sock:receive_lines(1) + + if not status then + break + end + + response = response .. line + end + + -- Either we reached the end of the stream, or we got all we could + -- within the socket timeout + if line == "EOF" or (line == "TIMEOUT" and response ~= "") then + return true, response + end + + return false, line +end + +get_banner = function(host, port, opts) + opts = initopts(opts) + + local status, sock = setup_connect(host, port, opts) + local ret + + if not status then + -- sock is an error message in this case + return status, sock + end + + status, ret = read(sock, opts) + + sock:close() + + return status, ret +end + +exchange = function(host, port, data, opts) + opts = initopts(opts) + + local status, sock = setup_connect(host, port, opts) + local ret + + if not status then + -- sock is an error message in this case + return status, sock + end + + status, ret = sock:send(data) + + if not status then + sock:close() + return status, ret + end + + status, ret = read(sock, opts) + + sock:close() + + return status, ret +end + diff --git a/scripts/HTTP_open_proxy.nse b/scripts/HTTP_open_proxy.nse index 3b164dd39..9211a0c30 100644 --- a/scripts/HTTP_open_proxy.nse +++ b/scripts/HTTP_open_proxy.nse @@ -8,6 +8,7 @@ id="Open Proxy Test" description="Test if a discovered proxy is open to us by connecting to www.google.com and checking for the 'Server: GWS/' header response." categories = {"default", "intrusive"} +require "comm" -- I found a nice explode() function in lua-users' wiki. I had to fix it, though. -- http://lua-users.org/wiki/LuaRecipes @@ -39,29 +40,22 @@ portrule = function(host, port) end action = function(host, port) - local socket = nmap.new_socket() - local result - local status = true local response local i -- We will return this if we don't find "^Server: GWS" in response headers local retval - socket:set_timeout(10000); - socket:connect(host.ip, port.number, port.protocol) - -- Ask proxy to open www.google.com - socket:send("GET http://www.google.com HTTP/1.0\r\nHost: www.google.com\r\n\r\n") - --- read the response, if any - status, result = socket:receive_lines(1) + local req = "GET http://www.google.com HTTP/1.0\r\nHost: www.google.com\r\n\r\n" + local status, result = comm.exchange(host, port, req, {proto=port.protocol, timeout=10000}) --- Explode result into the response table - if (status == false) or (result == "TIMEOUT") then - else - response = explode("\n",result) + if not status then + return end +-- Explode result into the response table + response = explode("\n",result) + -- Now, search for Server: GWS until headers (or table) end. i = 0 while true do @@ -74,7 +68,5 @@ action = function(host, port) end end --- close the socket and exit, returning the retval string. - socket:close() return retval end diff --git a/scripts/HTTPtrace.nse b/scripts/HTTPtrace.nse index 40e0bc3a2..9f35972a5 100644 --- a/scripts/HTTPtrace.nse +++ b/scripts/HTTPtrace.nse @@ -18,6 +18,7 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"discovery"} +require "comm" require "shortport" require "stdnse" @@ -76,31 +77,14 @@ end portrule = shortport.port_or_service({80, 8080}, "http") action = function(host, port) - local cmd, response - local socket + local cmd = "TRACE / HTTP/1.0\r\n\r\n" - socket = nmap.new_socket() + local status, response = comm.exchange(host, port, cmd, {timeout=5000}) - socket:connect(host.ip, port.number) - - cmd = "TRACE / HTTP/1.0\r\n\r\n" - - socket:send(cmd) - - response = "" - - while true do - local status, lines = socket:receive_lines(1) - - if not status then - break - end - - response = response .. lines + if not status then + return end - socket:close() - return validate(response, cmd) end diff --git a/scripts/MySQLinfo.nse b/scripts/MySQLinfo.nse index 089153020..401a770a1 100644 --- a/scripts/MySQLinfo.nse +++ b/scripts/MySQLinfo.nse @@ -18,6 +18,7 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = { "default", "discovery", "safe" } require 'bit' +require 'comm' -- Grabs NUL-terminated string local getstring = function(orig) @@ -105,28 +106,14 @@ portrule = function(host, port) end action = function(host, port) - local sock - local response = "" local output = "" - sock = nmap.new_socket() + local status, response = comm.get_banner(host, port, {timeout=5000}) - sock:set_timeout(5000) - - sock:connect(host.ip, port.number) - - while true do - local status, line = sock:receive_lines(1) - - if not status then - break - end - - response = response .. line + if not status then + return end - sock:close() - local length = ntoh3(response:sub(1, 3)) if length ~= response:len() - 4 then diff --git a/scripts/PPTPversion.nse b/scripts/PPTPversion.nse index bdc32322a..ade1fc806 100644 --- a/scripts/PPTPversion.nse +++ b/scripts/PPTPversion.nse @@ -11,6 +11,8 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"version"} +require "comm" + portrule = function(host, port) if port.number == 1723 @@ -24,23 +26,6 @@ portrule = function(host, port) end action = function(host, port) - - -- create the socket used for our connection - local socket = nmap.new_socket() - - -- set a reasonable timeout value - socket:set_timeout(5000) - - -- do some exception handling / cleanup - local catch = function() - socket:close() - end - - local try = nmap.new_try(catch) - - -- connect to the potential PPTP service - try(socket:connect(host.ip, port.number, "tcp")) - local payload -- build a PPTP Start-Control-Connection-Request packet @@ -67,24 +52,9 @@ action = function(host, port) payload = payload .. "\000\000\000\000\000\000\000\000" -- padding for vendor name payload = payload .. "\000\000\000\000" -- padding for vendor name - try(socket:send(payload)) - - local status - local response - - -- read in any response we might get - status, response = socket:receive_bytes(1) + local try = nmap.new_try() + local response = try(comm.exchange(host, port, payload, {bytes=1, timeout=5000})) - if (not status) then - return - end - - if (response == "TIMEOUT") then - return - end - - try(socket:close()) - local result -- check to see if the packet we got back matches the beginning of a PPTP Start-Control-Connection-Reply packet diff --git a/scripts/chargenTest.nse b/scripts/chargenTest.nse index b5da712ba..c138c3653 100644 --- a/scripts/chargenTest.nse +++ b/scripts/chargenTest.nse @@ -8,20 +8,15 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"demo"} +require "comm" require "shortport" portrule = shortport.port_or_service(19, "chargen", "udp") action = function(host, port) - local socket = nmap.new_socket() - socket:connect(host.ip, port.number, "udp") - socket:send("dummy") - local status, result = socket:receive_lines(1); - socket:close() + local status, result = comm.exchange(host, port, "dummy", {lines=1, proto="udp"}) - if (result ~= nil) then + if status then return "Chargen: success" - else - return "Chargen: something went wrong" end end diff --git a/scripts/daytimeTest.nse b/scripts/daytimeTest.nse index 1e1d6cafb..320ec21ae 100644 --- a/scripts/daytimeTest.nse +++ b/scripts/daytimeTest.nse @@ -8,18 +8,15 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"demo"} +require "comm" require "shortport" portrule = shortport.port_or_service(13, "daytime", "udp") action = function(host, port) - local socket = nmap.new_socket() - socket:connect(host.ip, port.number, "udp") - socket:send("dummy") - local status, result = socket:receive_lines(1); - socket:close() + local status, result = comm.exchange(host, port, "dummy", {lines=1, proto="udp"}) - if (result ~= nil) then + if status then return "Daytime: " .. result end end diff --git a/scripts/dns-test-open-recursion.nse b/scripts/dns-test-open-recursion.nse index 894194e58..d495f1c32 100644 --- a/scripts/dns-test-open-recursion.nse +++ b/scripts/dns-test-open-recursion.nse @@ -9,6 +9,7 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"default", "intrusive"} require "bit" +require "comm" require "shortport" portrule = shortport.portnumber(53, "udp") @@ -18,12 +19,11 @@ action = function(host, port) -- generate dns query, Transaction-ID 0xdead, www.wikipedia.org (type A, class IN) local request = string.char(0xde, 0xad, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03) .. "www" .. string.char(0x09) .. "wikipedia" .. string.char(0x03) .. "org" .. string.char(0x00, 0x00, 0x01, 0x00, 0x01) - local socket = nmap.new_socket() - socket:connect(host.ip, port.number, "udp") - socket:send(request) + local status, result = comm.exchange(host, port, request, {proto="udp"}) - local status, result = socket:receive(); - socket:close() + if not status then + return + end -- parse response for dns flags if (bit.band(string.byte(result,3), 0x80) == 0x80 diff --git a/scripts/echoTest.nse b/scripts/echoTest.nse index 431428aa5..6e5f15cc9 100644 --- a/scripts/echoTest.nse +++ b/scripts/echoTest.nse @@ -9,21 +9,17 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"demo"} +require "comm" require "shortport" portrule = shortport.port_or_service(7, "echo", "udp") action = function(host, port) local echostr = "hello there" - local socket = nmap.new_socket() - socket:connect(host.ip, port.number, "udp") - socket:send(echostr) - local status, result = socket:receive_lines(1); - socket:close() + + local status, result = comm.exchange(host, port, echostr, {lines=1, proto="udp"}) if (result == echostr) then return "UDP Echo: correct response" end - - return end diff --git a/scripts/finger.nse b/scripts/finger.nse index 5aeca9550..d24c91d85 100644 --- a/scripts/finger.nse +++ b/scripts/finger.nse @@ -8,29 +8,13 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"default", "discovery"} +require "comm" require "shortport" portrule = shortport.port_or_service(79, "finger") action = function(host, port) - local socket = nmap.new_socket() - local results = "" - local status = true + local try = nmap.new_try() - local err_catch = function() - socket:close() - end - - local try = nmap.new_try(err_catch()) - - socket:set_timeout(5000) - try(socket:connect(host.ip, port.number, port.protocol)) - try(socket:send("\r\n")) - - status, results = socket:receive_lines(100) - socket:close() - - if not(status) then - return results - end + return try(comm.exchange(host, port, "\r\n", {lines=100, proto=port.protocol, timeout=5000})) end diff --git a/scripts/iax2Detect.nse b/scripts/iax2Detect.nse index 5ee922bf3..28026e1ed 100644 --- a/scripts/iax2Detect.nse +++ b/scripts/iax2Detect.nse @@ -9,43 +9,36 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"version"} +require "comm" require "shortport" portrule = shortport.portnumber(4569, "udp") action = function(host, port) - local soc = nmap.new_socket() - soc:set_timeout(10000) - local conn = soc:connect(host.ip, port.number, port.protocol) + -- see http://www.cornfed.com/iax.pdf for all options. + local poke = string.char(0x80, 0x00, 0x00, 0x00) + poke = poke .. string.char(0x00, 0x00, 0x00, 0x00) + poke = poke .. string.char(0x00, 0x00, 0x06, 0x1e) - if (conn) then - -- see http://www.cornfed.com/iax.pdf for all options. - local poke = string.char(0x80, 0x00, 0x00, 0x00) - poke = poke .. string.char(0x00, 0x00, 0x00, 0x00) - poke = poke .. string.char(0x00, 0x00, 0x06, 0x1e) - soc:send(poke) - - local status, recv - status, recv = soc:receive_bytes(1) - - if (string.len(recv)) == 12 then - local byte11 = string.format("%02X", string.byte(recv, 11)) - local byte12 = string.format("%02X", string.byte(recv, 12)) - - -- byte11 must be \x06 IAX Control Frame - -- and byte12 must be \x03 or \x04 - if ((byte11 == "06") and - (byte12 == ("03" or "04"))) - then - nmap.set_port_state(host, port, "open") - port.version.name = "iax2" - nmap.set_port_version(host, port, "hardmatched") - end - - end - - soc:close() + local status, recv = comm.exchange(host, port, poke, {bytes=1,proto=port.protocol,timeout=10000}) + if not status then + return end + if (string.len(recv)) == 12 then + local byte11 = string.format("%02X", string.byte(recv, 11)) + local byte12 = string.format("%02X", string.byte(recv, 12)) + + -- byte11 must be \x06 IAX Control Frame + -- and byte12 must be \x03 or \x04 + if ((byte11 == "06") and + (byte12 == ("03" or "04"))) + then + nmap.set_port_state(host, port, "open") + port.version.name = "iax2" + nmap.set_port_version(host, port, "hardmatched") + end + + end end diff --git a/scripts/ircZombieTest.nse b/scripts/ircZombieTest.nse index c3c672f94..3a411a7c1 100644 --- a/scripts/ircZombieTest.nse +++ b/scripts/ircZombieTest.nse @@ -9,23 +9,15 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"malware"} +require "comm" require "shortport" portrule = shortport.port_or_service(113, "auth") action = function(host, port) - local status = 0 - local owner = "" + local status, owner = comm.get_banner(host, port, {lines=1}) - local client_ident = nmap.new_socket() - - client_ident:connect(host.ip, port.number) - - status, owner = client_ident:receive_lines(1) - - client_ident:close() - - if owner == "TIMEOUT" then + if not status then return end diff --git a/scripts/nbstat.nse b/scripts/nbstat.nse index 1dcf95e45..e34cb3782 100644 --- a/scripts/nbstat.nse +++ b/scripts/nbstat.nse @@ -11,6 +11,8 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"default", "discovery", "safe"} +require "comm" + -- I have excluded the port function param because it doesn't make much sense -- for a hostrule. It works without warning. The NSE documentation is -- not explicit enough in this regard. @@ -49,51 +51,23 @@ end -- Again, I have excluded the port param. Is this okay on a hostrule? action = function(host) - local socket = nmap.new_socket() - - socket:set_timeout(5000) - - local result - local status = true - - status, result = socket:connect(host.ip, 137, "udp") - - if (not status) then - -- Can a UDP connect ever fail? - return - end - -- This is the UDP NetBIOS request packet. I didn't feel like -- actually generating a new one each time so this has been shamelessly -- copied from a packet dump of nbtscan. -- See http://www.unixwiz.net/tools/nbtscan.html for code. -- The magic number in this code is \003\097. - status, result = socket:send( + local data = "\003\097\000\016\000\001\000\000" .. "\000\000\000\000\032\067\075\065" .. "\065\065\065\065\065\065\065\065" .. "\065\065\065\065\065\065\065\065" .. "\065\065\065\065\065\065\065\065" .. "\065\065\065\065\065\000\000\033" .. - "\000\001") + "\000\001" + + local status, result = comm.exchange(host, 137, data, {bytes=1, proto="udp", timeout=5000}) if (not status) then - -- Can the first UDP send ever fail? - return - end - - -- this receive_bytes will consume all the input available - -- with a minimum of 1 byte. - status, result = socket:receive_bytes(1); - - -- We don't need this socket anymore - socket:close() - - if (not status) then - return - end - - if (result == "TIMEOUT") then return end diff --git a/scripts/ripeQuery.nse b/scripts/ripeQuery.nse index bf21cf36d..a25ebc783 100644 --- a/scripts/ripeQuery.nse +++ b/scripts/ripeQuery.nse @@ -1,3 +1,4 @@ +require "comm" require "ipOps" id = "RIPE query" @@ -12,24 +13,11 @@ hostrule = function(host, port) end action = function(host, port) - local socket = nmap.new_socket() - local status, line - local result = "" + local status, result = comm.exchange("whois.ripe.net", 43, host.ip .. "\n") - socket:connect("whois.ripe.net", 43) --- socket:connect("193.0.0.135", 43) - socket:send(host.ip .. "\n") - - while true do - local status, lines = socket:receive_lines(1) - - if not status then - break - else - result = result .. lines - end + if not status then + return end - socket:close() local value = string.match(result, "role:(.-)\n") diff --git a/scripts/rpcinfo.nse b/scripts/rpcinfo.nse index 3a5ba6607..a0e8676bc 100644 --- a/scripts/rpcinfo.nse +++ b/scripts/rpcinfo.nse @@ -5,6 +5,7 @@ author = "Sven Klemm " license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"default","safe","discovery"} +require "comm" require "shortport" require "packet" require "datafiles" @@ -12,14 +13,12 @@ require "datafiles" portrule = shortport.port_or_service(111, "rpcbind") action = function(host, port) - local try, catch + local try local transaction_id = "nmap" - local socket = nmap.new_socket() local result = " \n" local rpc_numbers - catch = function() socket:close() end - try = nmap.new_try( catch ) + try = nmap.new_try() rpc_numbers = try(datafiles.parse_rpc()) local request = string.char(0x80,0,0,40) -- fragment header @@ -29,16 +28,8 @@ action = function(host, port) request = request .. "\0\0\0\2\0\0\0\4" -- programm version (2) procedure dump(4) request = request .. "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"-- Credentials and verifier - socket:set_timeout(1000) - try( socket:connect(host.ip, port.number) ) - try( socket:send( request ) ) - local status, answer, answer_part - status, answer = socket:receive_bytes( 1 ) - while status do - status, answer_part = socket:receive_bytes( 1 ) - if status then answer = answer .. answer_part end - end - socket:close() + local answer = try(comm.exchange(host, port, request, {timeout=1000})) + local answer_part local fragment_length = answer:byte(4) + answer:byte(3) * 256 + answer:byte(2) * 65536 if answer:sub(5,8) == transaction_id and answer:byte(12) == 1 and answer:byte(16) == 0 and answer:byte(28) == 0 then diff --git a/scripts/showSMTPVersion.nse b/scripts/showSMTPVersion.nse index f346c82fe..f034da515 100644 --- a/scripts/showSMTPVersion.nse +++ b/scripts/showSMTPVersion.nse @@ -8,24 +8,18 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"demo"} +require "comm" require "shortport" portrule = shortport.port_or_service(25, "smtp") action = function(host, port) - - local client = nmap.new_socket() + local status, result = comm.get_banner(host, port, {lines=1}) - client:connect(host.ip, port.number) - - local status, result = client:receive_lines(1); - - client:close() - - if result ~= nil then - result = string.gsub(result, "\n", "") + if not status then + return end - return result + return string.gsub(result, "\n", "") end diff --git a/scripts/skype_v2-version.nse b/scripts/skype_v2-version.nse index 532d10141..617c79ae6 100644 --- a/scripts/skype_v2-version.nse +++ b/scripts/skype_v2-version.nse @@ -3,6 +3,7 @@ description="Determines if remote service is Skype protocol version 2" author = "Brandon Enright " license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"version"} +require "comm" portrule = function(host, port) if (port.number == 80 or @@ -22,42 +23,25 @@ portrule = function(host, port) end action = function(host, port) - - local socket = nmap.new_socket() - local result; - local status = true - - socket:connect(host.ip, port.number, port.protocol) - socket:send("GET / HTTP/1.0\r\n\r\n") - - status, result = socket:receive_bytes(26); + local status, result = comm.exchange(host, port, "GET / HTTP/1.0\r\n\r\n", {bytes=26, proto=port.protocol}) if (not status) then - socket:close() return end if (result ~= "HTTP/1.0 404 Not Found\r\n\r\n") then - socket:close() return end - socket:close(); - -- So far so good, now see if we get random data for another request - socket:connect(host.ip, port.number, port.protocol) - socket:send("random data\r\n\r\n") - - status, result = socket:receive_bytes(15); + status, result = comm.exchange(host, port, "random data\r\n\r\n", {bytes=15, proto=port.protocol}) if (not status) then - socket:close() return end if string.match(result, "[^%s!-~].*[^%s!-~].*[^%s!-~]") then - socket:close() port.version.name = "skype2" port.version.product = "Skype" port.version.confidence = 10 @@ -67,7 +51,5 @@ action = function(host, port) -- return "Skype v2 server detected" end - socket:close(); - return end