xmpp-fingerprint.lua

1
#!/usr/bin/env lua
2
 
3
function softreq(...) local ok, lib =  pcall(require, ...); if ok then return lib; else 
4
return nil, lib; end end
5
 
6
-- Required to be able to find packages installed with luarocks
7
if not softreq "luarocks.loader" then -- LuaRocks 2.x
8
        softreq "luarocks.require"; -- LuaRocks <1.x
9
end
10
 
11
local socket = softreq "socket";
12
 
13
-- Defaults --
14
default_port = 5222;
15
default_namespace = "jabber:client";
16
default_timeout = 1.5;
17
 
18
-- Print debug output --
19
function debug() end -- Keep debug output quiet by default
20
 
21
-- Command-line flag handlers --
22
local flags = {};
23
function flags.d() -- Show debug
24
	debug = print;
25
end
26
 
27
function flags.s() -- Use s2s
28
	-- Use s2s
29
	default_port = 5269;
30
	default_namespace = "jabber:server";
31
end
32
 
33
function flags.t(arg)
34
	default_timeout = tonumber(arg[1] or 1);
35
	return 1;
36
end
37
 
38
function flags.help()
39
	print("XMPP Server Fingerprinter");
40
	print("(C) 2010 Matthew Wild. Licensed under the MIT/X11 License");
41
	print("");
42
	print("Syntax: "..arg[0].." [OPTIONS] ADDRESS [PORT]");
43
	print("");
44
	print("ADDRESS must be the network address or IP to connect to, SRV lookup");
45
	print("is not supported.");
46
	print("");
47
	print("PORT is the port number to connect to. Defaults to 5222, or 5269 for");
48
	print("server-to-server testing.");
49
	print("");
50
	print("Options:");
51
	print("    -s:", "Server-to-server port testing (less reliable)");
52
	print("-t NUM:", "Set timeout to NUM seconds");
53
	print("    -d:", "Enable debug output (useful for adding new fingerprints)");
54
	print("");
55
end
56
 
57
-- Process flags --
58
local function process_args(arg)
59
	local f = arg[1] and arg[1]:match("^%-%-?(%S+)");
60
	if f then
61
		table.remove(arg, 1);
62
		if flags[f] then
63
			local used = flags[f](arg);
64
			if used then
65
				for i=1,used do
66
					table.remove(arg, 1);
67
				end
68
			end
69
		end
70
		return process_args(arg);
71
	end
72
end
73
if #arg > 0 then
74
	process_args(arg);
75
else
76
	flags.help();
77
	os.exit(1);
78
end
79
 
80
if #arg < 1 then
81
	io.stderr:write("ERROR: No server address specified.\n");
82
	os.exit(1);
83
end
84
 
85
local received_cache = {};
86
local function _test(server, port, question, answer)
87
	local received = received_cache[question];
88
	if not received then
89
		debug("||", "Connecting to "..server..":"..port.."...");
90
		local conn = assert(socket.connect(server, port), "Failed to connect to "..arg[1]);
91
		conn:send(question);
92
		debug(">>", tostring(question));
93
		conn:settimeout(default_timeout);
94
		local data, err, partial = conn:receive("*a");
95
		received = data or partial or "";
96
		conn:close();
97
		received_cache[question] = received;
98
		if received ~= "" then
99
			debug("<<", received);
100
		else
101
			debug("!!", tostring(err));
102
		end
103
	end
104
	if received == "" then
105
		return answer == false
106
	elseif answer then
107
		return received:match(answer);
108
	end
109
end
110
 
111
function test(question, answer)
112
	return _test(arg[1], tonumber(arg[2]) or default_port, question, answer);
113
end
114
 
115
function server_comment(comment)
116
	table.insert(getfenv(2).server_comments, comment);
117
end
118
 
119
function literal(s)
120
	return s:gsub("%p", "%%%1");
121
end
122
 
123
-----
124
q_invalid_xml = "abc\n";
125
q_empty_message_tag = "<message/>";
126
q_invalid_host = "<stream:stream xmlns='"..default_namespace.."' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' id='abc' to='0.0.0.0'>";
127
-----
128
 
129
testers = {};
130
 
131
dofile "servers/ejabberd.lua"
132
dofile "servers/google_talk.lua"
133
dofile "servers/isode_mlink.lua"
134
dofile "servers/jabberd14.lua"
135
dofile "servers/jabberd2.lua"
136
dofile "servers/prosody.lua"
137
dofile "servers/tigase.lua"
138
dofile "servers/djabberd.lua"
139
dofile "servers/openfire.lua"
140
dofile "servers/jabber_xcp.lua"
141
dofile "servers/soapbox.lua"
142
dofile "servers/facebook.lua"
143
 
144
function test_fingerprint(server)
145
	local tester = testers[server];
146
	local env = setmetatable({server_comments = {}}, { __index = _G });
147
	setfenv(tester, env);
148
	local ok, err = pcall(tester);
149
	if not ok then
150
		print("Error while testing for "..server..": "..err);
151
	end
152
	return env;
153
end
154
 
155
------------------------------------------------------------------
156
 
157
local results = {};
158
for server in pairs(testers) do
159
	local res = test_fingerprint(server);
160
	if res.server_name then
161
		table.insert(results, res);
162
	end
163
end
164
 
165
if #results > 1 then
166
	print("Note... multiple matches, could be any:");
167
end
168
 
169
for _, result in ipairs(results) do
170
	io.write(result.server_name);
171
	if result.server_min_version then
172
		io.write(", version "..result.server_min_version.." or later");
173
	elseif result.server_version then
174
		io.write(", version "..result.server_version);
175
	end
176
	io.write("\n");
177
	if #result.server_comments > 0 then
178
		print("   Notes:\n", table.concat(result.server_comments, "\n      "));
179
	end
180
end