Comparison

mod_tcpproxy/mod_tcpproxy.lua @ 147:4db80a46b064

mod_tcpproxy: Initial commit. The moment you didn't know you've been waiting for is here... the dawn of TCP over XMPP.
author Matthew Wild <mwild1@gmail.com>
date Tue, 13 Apr 2010 04:38:07 +0100
child 153:31e24026e4fd
comparison
equal deleted inserted replaced
146:2620bc59cca3 147:4db80a46b064
1 local st = require "util.stanza";
2
3 local xmlns_ibb = "http://jabber.org/protocol/ibb";
4 local xmlns_tcp = "http://prosody.im/protocol/tcpproxy";
5
6 local host_attr, port_attr = xmlns_tcp.."\1host", xmlns_tcp.."\1port";
7
8 local base64 = require "util.encodings".base64;
9 local b64, unb64 = base64.encode, base64.decode;
10
11 local host = module.host;
12
13 local open_connections = {};
14
15 local function new_session(jid, sid, conn)
16 if not open_connections[jid] then
17 open_connections[jid] = {};
18 end
19 open_connections[jid][sid] = conn;
20 end
21 local function close_session(jid, sid)
22 if open_connections[jid] then
23 open_connections[jid][sid] = nil;
24 if next(open_connections[jid]) == nil then
25 open_connections[jid] = nil;
26 end
27 return true;
28 end
29 end
30
31 function proxy_component(origin, stanza)
32 local ibb_tag = stanza.tags[1];
33 if (not (stanza.name == "iq" and stanza.attr.type == "set")
34 and stanza.name ~= "message")
35 or
36 (not (ibb_tag)
37 or ibb_tag.attr.xmlns ~= xmlns_ibb) then
38 if stanza.attr.type ~= "error" then
39 origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
40 end
41 return;
42 end
43
44 if ibb_tag.name == "open" then
45 -- Starting a new stream
46 local to_host, to_port = ibb_tag.attr[host_attr], ibb_tag.attr[port_attr];
47 local jid, sid = stanza.attr.from, ibb_tag.attr.sid;
48 if not (to_host and to_port) then
49 return origin.send(st.error_reply(stanza, "modify", "bad-request", "No host/port specified"));
50 elseif not sid or sid == "" then
51 return origin.send(st.error_reply(stanza, "modify", "bad-request", "No sid specified"));
52 elseif ibb_tag.attr.stanza ~= "message" then
53 return origin.send(st.error_reply(stanza, "modify", "bad-request", "Only 'message' stanza transport is supported"));
54 end
55 local conn, err = socket.tcp();
56 if not conn then
57 return origin.send(st.error_reply(stanza, "wait", "resource-constraint", err));
58 end
59 conn:settimeout(0);
60
61 local success, err = conn:connect(to_host, to_port);
62 if not success and err ~= "timeout" then
63 return origin.send(st.error_reply(stanza, "wait", "remote-server-not-found", err));
64 end
65
66 local listener,seq = {}, 0;
67 function listener.onconnect(conn)
68 origin.send(st.reply(stanza));
69 end
70 function listener.onincoming(conn, data)
71 origin.send(st.message({to=jid,from=host})
72 :tag("data", {xmlns=xmlns_ibb,seq=seq,sid=sid})
73 :text(b64(data)));
74 seq = seq + 1;
75 end
76 function listener.ondisconnect(conn, err)
77 origin.send(st.message({to=jid,from=host})
78 :tag("close", {xmlns=xmlns_ibb,sid=sid}));
79 close_session(jid, sid);
80 end
81
82 conn = server.wrapclient(conn, to_host, to_port, listener, "*a" );
83 new_session(jid, sid, conn);
84 elseif ibb_tag.name == "data" then
85 local conn = open_connections[stanza.attr.from][ibb_tag.attr.sid];
86 if conn then
87 conn:write(unb64(ibb_tag:get_text()));
88 else
89 return origin.send(st.error_reply(stanza, "cancel", "item-not-found"));
90 end
91 elseif ibb_tag.name == "close" then
92 if close_session(stanza.attr.from, ibb_tag.attr.sid) then
93 origin.send(st.reply(stanza));
94 else
95 return origin.send(st.error_reply(stanza, "cancel", "item-not-found"));
96 end
97 end
98 end
99
100 require "core.componentmanager".register_component(host, proxy_component);