# HG changeset patch # User Matthew Wild # Date 1271129887 -3600 # Node ID 4db80a46b064566760e1c21e8dce0831760e40b9 # Parent 2620bc59cca36d41dc4b5d32c040b0e5538dcc17 mod_tcpproxy: Initial commit. The moment you didn't know you've been waiting for is here... the dawn of TCP over XMPP. diff -r 2620bc59cca3 -r 4db80a46b064 mod_tcpproxy/mod_tcpproxy.lua --- /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);