--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_tcpproxy/mod_tcpproxy.lua Tue Apr 13 04:38:07 2010 +0100
@@ -0,0 +1,100 @@
+local st = require "util.stanza";
+
+local xmlns_ibb = "http://jabber.org/protocol/ibb";
+local xmlns_tcp = "http://prosody.im/protocol/tcpproxy";
+
+local host_attr, port_attr = xmlns_tcp.."\1host", xmlns_tcp.."\1port";
+
+local base64 = require "util.encodings".base64;
+local b64, unb64 = base64.encode, base64.decode;
+
+local host = module.host;
+
+local open_connections = {};
+
+local function new_session(jid, sid, conn)
+ if not open_connections[jid] then
+ open_connections[jid] = {};
+ end
+ open_connections[jid][sid] = conn;
+end
+local function close_session(jid, sid)
+ if open_connections[jid] then
+ open_connections[jid][sid] = nil;
+ if next(open_connections[jid]) == nil then
+ open_connections[jid] = nil;
+ end
+ return true;
+ end
+end
+
+function proxy_component(origin, stanza)
+ local ibb_tag = stanza.tags[1];
+ if (not (stanza.name == "iq" and stanza.attr.type == "set")
+ and stanza.name ~= "message")
+ or
+ (not (ibb_tag)
+ or ibb_tag.attr.xmlns ~= xmlns_ibb) then
+ if stanza.attr.type ~= "error" then
+ origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
+ end
+ return;
+ end
+
+ if ibb_tag.name == "open" then
+ -- Starting a new stream
+ local to_host, to_port = ibb_tag.attr[host_attr], ibb_tag.attr[port_attr];
+ local jid, sid = stanza.attr.from, ibb_tag.attr.sid;
+ if not (to_host and to_port) then
+ return origin.send(st.error_reply(stanza, "modify", "bad-request", "No host/port specified"));
+ elseif not sid or sid == "" then
+ return origin.send(st.error_reply(stanza, "modify", "bad-request", "No sid specified"));
+ elseif ibb_tag.attr.stanza ~= "message" then
+ return origin.send(st.error_reply(stanza, "modify", "bad-request", "Only 'message' stanza transport is supported"));
+ end
+ local conn, err = socket.tcp();
+ if not conn then
+ return origin.send(st.error_reply(stanza, "wait", "resource-constraint", err));
+ end
+ conn:settimeout(0);
+
+ local success, err = conn:connect(to_host, to_port);
+ if not success and err ~= "timeout" then
+ return origin.send(st.error_reply(stanza, "wait", "remote-server-not-found", err));
+ end
+
+ local listener,seq = {}, 0;
+ function listener.onconnect(conn)
+ origin.send(st.reply(stanza));
+ end
+ function listener.onincoming(conn, data)
+ origin.send(st.message({to=jid,from=host})
+ :tag("data", {xmlns=xmlns_ibb,seq=seq,sid=sid})
+ :text(b64(data)));
+ seq = seq + 1;
+ end
+ function listener.ondisconnect(conn, err)
+ origin.send(st.message({to=jid,from=host})
+ :tag("close", {xmlns=xmlns_ibb,sid=sid}));
+ close_session(jid, sid);
+ end
+
+ conn = server.wrapclient(conn, to_host, to_port, listener, "*a" );
+ new_session(jid, sid, conn);
+ elseif ibb_tag.name == "data" then
+ local conn = open_connections[stanza.attr.from][ibb_tag.attr.sid];
+ if conn then
+ conn:write(unb64(ibb_tag:get_text()));
+ else
+ return origin.send(st.error_reply(stanza, "cancel", "item-not-found"));
+ end
+ elseif ibb_tag.name == "close" then
+ if close_session(stanza.attr.from, ibb_tag.attr.sid) then
+ origin.send(st.reply(stanza));
+ else
+ return origin.send(st.error_reply(stanza, "cancel", "item-not-found"));
+ end
+ end
+end
+
+require "core.componentmanager".register_component(host, proxy_component);