Software /
code /
verse
File
plugins/proxy65.lua @ 445:b119dc4d8bc2
plugins.smacks: Don't warn about zero stanzas acked
It's only if the count somehow goes backwards that something is really
wrong. An ack for zero stanzas is fine and we don't need to do anything.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Thu, 10 Jun 2021 11:58:23 +0200 |
parent | 395:e86144a4eaa1 |
child | 457:73d4eb93657b |
line wrap: on
line source
local verse = require "verse"; local uuid = require "util.uuid"; local sha1 = require "util.hashes".sha1; local proxy65_mt = {}; proxy65_mt.__index = proxy65_mt; local xmlns_bytestreams = "http://jabber.org/protocol/bytestreams"; local negotiate_socks5; function verse.plugins.proxy65(stream) stream.proxy65 = setmetatable({ stream = stream }, proxy65_mt); stream.proxy65.available_streamhosts = {}; local outstanding_proxies = 0; stream:hook("disco/service-discovered/proxy", function (service) -- Fill list with available proxies if service.type == "bytestreams" then outstanding_proxies = outstanding_proxies + 1; stream:send_iq(verse.iq({ to = service.jid, type = "get" }) :tag("query", { xmlns = xmlns_bytestreams }), function (result) outstanding_proxies = outstanding_proxies - 1; if result.attr.type == "result" then local streamhost = result:get_child("query", xmlns_bytestreams) :get_child("streamhost").attr; stream.proxy65.available_streamhosts[streamhost.jid] = { jid = streamhost.jid; host = streamhost.host; port = tonumber(streamhost.port); }; end if outstanding_proxies == 0 then stream:event("proxy65/discovered-proxies", stream.proxy65.available_streamhosts); end end); end end); stream:hook("iq/"..xmlns_bytestreams, function (request) local conn = verse.new(nil, { initiator_jid = request.attr.from, streamhosts = {}, current_host = 0; }); -- Parse hosts from request for tag in request.tags[1]:childtags() do if tag.name == "streamhost" then table.insert(conn.streamhosts, tag.attr); end end --Attempt to connect to the next host local function attempt_next_streamhost() -- First connect, or the last connect failed if conn.current_host < #conn.streamhosts then conn.current_host = conn.current_host + 1; conn:connect( conn.streamhosts[conn.current_host].host, conn.streamhosts[conn.current_host].port ); negotiate_socks5(stream, conn, request.tags[1].attr.sid, request.attr.from, stream.jid); return true; -- Halt processing of disconnected event end -- All streamhosts tried, none successful conn:unhook("disconnected", attempt_next_streamhost); stream:send(verse.error_reply(request, "cancel", "item-not-found")); -- Let disconnected event fall through to user handlers... end function conn:accept() conn:hook("disconnected", attempt_next_streamhost, 100); -- When this event fires, we're connected to a streamhost conn:hook("connected", function () conn:unhook("disconnected", attempt_next_streamhost); -- Send XMPP success notification local reply = verse.reply(request) :tag("query", request.tags[1].attr) :tag("streamhost-used", { jid = conn.streamhosts[conn.current_host].jid }); stream:send(reply); end, 100); attempt_next_streamhost(); end function conn:refuse() -- FIXME: XMPP refused reply end stream:event("proxy65/request", conn); end); end function proxy65_mt:new(target_jid, proxies) local conn = verse.new(nil, { target_jid = target_jid; bytestream_sid = uuid.generate(); }); local request = verse.iq{type="set", to = target_jid} :tag("query", { xmlns = xmlns_bytestreams, mode = "tcp", sid = conn.bytestream_sid }); for _, proxy in ipairs(proxies or self.proxies) do request:tag("streamhost", proxy):up(); end self.stream:send_iq(request, function (reply) if reply.attr.type == "error" then local type, condition, text = reply:get_error(); conn:event("connection-failed", { conn = conn, type = type, condition = condition, text = text }); else -- Target connected to streamhost, connect ourselves local streamhost_used = reply.tags[1]:get_child("streamhost-used"); -- if not streamhost_used then --FIXME: Emit error -- end conn.streamhost_jid = streamhost_used.attr.jid; local host, port; for _, proxy in ipairs(proxies or self.proxies) do if proxy.jid == conn.streamhost_jid then host, port = proxy.host, proxy.port; break; end end -- if not (host and port) then --FIXME: Emit error -- end conn:connect(host, port); local function handle_proxy_connected() conn:unhook("connected", handle_proxy_connected); -- Both of us connected, tell proxy to activate connection local activate_request = verse.iq{to = conn.streamhost_jid, type="set"} :tag("query", { xmlns = xmlns_bytestreams, sid = conn.bytestream_sid }) :tag("activate"):text(target_jid); self.stream:send_iq(activate_request, function (activated) if activated.attr.type == "result" then -- Connection activated, ready to use conn:event("connected", conn); -- else --FIXME: Emit error end end); return true; end conn:hook("connected", handle_proxy_connected, 100); negotiate_socks5(self.stream, conn, conn.bytestream_sid, self.stream.jid, target_jid); end end); return conn; end function negotiate_socks5(stream, conn, sid, requester_jid, target_jid) local hash = sha1(sid..requester_jid..target_jid); local function suppress_connected() conn:unhook("connected", suppress_connected); return true; end local function receive_connection_response(data) conn:unhook("incoming-raw", receive_connection_response); if data:sub(1, 2) ~= "\005\000" then return conn:event("error", "connection-failure"); end conn:event("connected"); return true; end local function receive_auth_response(data) conn:unhook("incoming-raw", receive_auth_response); if data ~= "\005\000" then -- SOCKSv5; "NO AUTHENTICATION" -- Server is not SOCKSv5, or does not allow no auth local err = "version-mismatch"; if data:sub(1,1) == "\005" then err = "authentication-failure"; end return conn:event("error", err); end -- Request SOCKS5 connection conn:send(string.char(0x05, 0x01, 0x00, 0x03, #hash)..hash.."\0\0"); --FIXME: Move to "connected"? conn:hook("incoming-raw", receive_connection_response, 100); return true; end conn:hook("connected", suppress_connected, 200); conn:hook("incoming-raw", receive_auth_response, 100); conn:send("\005\001\000"); -- SOCKSv5; 1 mechanism; "NO AUTHENTICATION" end