local comm = require "comm"
local math = require "math"
local nmap = require "nmap"
local pcre = require "pcre"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
description = [[
Gathers information from an IRC server.
It uses STATS, LUSERS, and other queries to obtain this information.
]]
---
-- @output
-- 6665/tcp open irc
-- | irc-info:
-- | server: asimov.freenode.net
-- | version: ircd-seven-1.1.3(20111112-b71671d1e846,charybdis-3.4-dev). asimov.freenode.net
-- | servers: 31
-- | ops: 36
-- | chans: 48636
-- | users: 84883
-- | lservers: 1
-- | lusers: 4350
-- | uptime: 511 days, 23:02:29
-- | source host: source.example.com
-- |_ source ident: NONE or BLOCKED
--@xmloutput
-- asimov.freenode.net
-- ircd-seven-1.1.3(20111112-b71671d1e846,charybdis-3.4-dev). asimov.freenode.net
-- 31
-- 36
-- 48636
-- 84883
-- 1
-- 4350
-- 511 days, 23:02:29
-- source.example.com
-- NONE or BLOCKED
author = "Doug Hoyte"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"default", "discovery", "safe"}
portrule = shortport.port_or_service({6666,6667,6697,6679},{"irc","ircs"})
local init = function()
-- Start of MOTD, we'll take the server name from here
nmap.registry.ircserverinfo_375 = nmap.registry.ircserverinfo_375
or pcre.new("^:([\\w-_.]+) 375", 0, "C")
-- MOTD could be missing, we want to handle that scenario as well
nmap.registry.ircserverinfo_422 = nmap.registry.ircserverinfo_422
or pcre.new("^:([\\w-_.]+) 422", 0, "C")
-- NICK already in use
nmap.registry.ircserverinfo_433 = nmap.registry.ircserverinfo_433
or pcre.new("^:[\\w-_.]+ 433", 0, "C")
-- PING/PONG
nmap.registry.ircserverinfo_ping = nmap.registry.ircserverinfo_ping
or pcre.new("^PING :(.+)", 0, "C")
-- Server version info
nmap.registry.ircserverinfo_351 = nmap.registry.ircserverinfo_351
or pcre.new("^:[\\w-_.]+ 351 \\w+ ([^:]+)", 0, "C")
-- Various bits of info
nmap.registry.ircserverinfo_251_efnet = nmap.registry.ircserverinfo_251_efnet
or pcre.new("^:[\\w-_.]+ 251 \\w+ :There are (\\d+) users and (\\d+) invisible on (\\d+) servers", 0, "C")
nmap.registry.ircserverinfo_251_ircnet = nmap.registry.ircserverinfo_251_ircnet
or pcre.new("^:[\\w-_.]+ 251 \\w+ :There are (\\d+) users and \\d+ services on (\\d+) servers", 0, "C")
nmap.registry.ircserverinfo_252 = nmap.registry.ircserverinfo_252
or pcre.new("^:[\\w-_.]+ 252 \\w+ (\\d+) :", 0, "C")
nmap.registry.ircserverinfo_254 = nmap.registry.ircserverinfo_254
or pcre.new("^:[\\w-_.]+ 254 \\w+ (\\d+) :", 0, "C")
nmap.registry.ircserverinfo_255_efnet = nmap.registry.ircserverinfo_255_efnet
or pcre.new("^:[\\w-_.]+ 255 \\w+ :I have (\\d+) clients and (\\d+) server", 0, "C")
nmap.registry.ircserverinfo_255_ircnet = nmap.registry.ircserverinfo_255_ircnet
or pcre.new("^:[\\w-_.]+ 255 \\w+ :I have (\\d+) users, \\d+ services and (\\d+) server", 0, "C")
nmap.registry.ircserverinfo_242 = nmap.registry.ircserverinfo_242
or pcre.new("^:[\\w-_.]+ 242 \\w+ :Server Up (\\d+ days, [\\d:]+)", 0, "C")
nmap.registry.ircserverinfo_352 = nmap.registry.ircserverinfo_352
or pcre.new("^:[\\w-_.]+ 352 \\w+ \\S+ (\\S+) ([\\w-_.]+)", 0, "C")
nmap.registry.ircserverinfo_error = nmap.registry.ircserverinfo_error
or pcre.new("^ERROR :(.*)", 0, "C")
end
action = function(host, port)
local sd = nmap.new_socket()
local curr_nick = random_nick()
local sver, shost, susers, sservers, schans, sircops, slusers, slservers, sup, serr
local myhost, myident
local s, e, t
local buf
local banner_timeout = 60
local make_output = function()
local o = stdnse.output_table()
if (not shost) then
if serr then
return "ERROR: " .. serr .. "\n"
else
return nil
end
end
o["server"] = shost
o["version"] = sver
o["servers"] = sservers
o["ops"] = sircops
o["chans"] = schans
o["users"] = susers
o["lservers"] = slservers
o["lusers"] = slusers
o["uptime"] = sup
o["source host"] = myhost
if myident and string.find(myident, "^~") then
o["source ident"] = "NONE or BLOCKED"
else
o["source ident"] = myident
end
return o
end
init()
local sd, line = comm.tryssl(host, port, "USER nmap +iw nmap :Nmap Wuz Here\nNICK " .. curr_nick .. "\n")
if not sd then return "Unable to open connection" end
-- set a healthy banner timeout
sd:set_timeout(banner_timeout * 1000)
buf = stdnse.make_buffer(sd, "\r?\n")
while true do
if (not line) then break end
-- This one lets us know we've connected, pre-PONGed, and got a NICK
s, e, t = nmap.registry.ircserverinfo_375:exec(line, 0, 0)
if (s) then
shost = string.sub(line, t[1], t[2])
sd:send("LUSERS\nVERSION\nSTATS u\nWHO " .. curr_nick .. "\nQUIT\n")
end
s, e, t = nmap.registry.ircserverinfo_422:exec(line, 0, 0)
if (s) then
shost = string.sub(line, t[1], t[2])
sd:send("LUSERS\nVERSION\nSTATS u\nWHO " .. curr_nick .. "\nQUIT\n")
end
s, e, t = nmap.registry.ircserverinfo_433:exec(line, 0, 0)
if (s) then
curr_nick = random_nick()
sd:send("NICK " .. curr_nick .. "\n")
end
s, e, t = nmap.registry.ircserverinfo_ping:exec(line, 0, 0)
if (s) then
sd:send("PONG :" .. string.sub(line, t[1], t[2]) .. "\n")
end
s, e, t = nmap.registry.ircserverinfo_351:exec(line, 0, 0)
if (s) then
sver = string.sub(line, t[1], t[2])
end
s, e, t = nmap.registry.ircserverinfo_251_efnet:exec(line, 0, 0)
if (s) then
susers = (string.sub(line, t[1], t[2]) + string.sub(line, t[3], t[4]))
sservers = string.sub(line, t[5], t[6])
end
s, e, t = nmap.registry.ircserverinfo_251_ircnet:exec(line, 0, 0)
if (s) then
susers = string.sub(line, t[1], t[2])
sservers = string.sub(line, t[3], t[4])
end
s, e, t = nmap.registry.ircserverinfo_252:exec(line, 0, 0)
if (s) then
sircops = string.sub(line, t[1], t[2])
end
s, e, t = nmap.registry.ircserverinfo_254:exec(line, 0, 0)
if (s) then
schans = string.sub(line, t[1], t[2])
end
s, e, t = nmap.registry.ircserverinfo_255_efnet:exec(line, 0, 0)
if (s) then
slusers = string.sub(line, t[1], t[2])
slservers = string.sub(line, t[3], t[4])
end
s, e, t = nmap.registry.ircserverinfo_255_ircnet:exec(line, 0, 0)
if (s) then
slusers = string.sub(line, t[1], t[2])
slservers = string.sub(line, t[3], t[4])
end
s, e, t = nmap.registry.ircserverinfo_242:exec(line, 0, 0)
if (s) then
sup = string.sub(line, t[1], t[2])
end
s, e, t = nmap.registry.ircserverinfo_352:exec(line, 0, 0)
if (s) then
myident = string.sub(line, t[1], t[2])
myhost = string.sub(line, t[3], t[4])
end
s, e, t = nmap.registry.ircserverinfo_error:exec(line, 0, 0)
if (s) then
serr = string.sub(line, t[1], t[2])
return make_output()
end
line = buf()
end
return make_output()
end
random_nick = function()
local nick = ""
-- NICKLEN is at least 9
for i = 0, 8, 1 do
nick = nick .. string.char(math.random(97, 122)) -- lowercase ascii
end
return nick
end