Software /
code /
verse
File
plugins/jingle_s5b.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 | 392:cdea6a28369e |
child | 457:73d4eb93657b |
line wrap: on
line source
local verse = require "verse"; local xmlns_s5b = "urn:xmpp:jingle:transports:s5b:1"; local xmlns_bytestreams = "http://jabber.org/protocol/bytestreams"; local sha1 = require "util.hashes".sha1; local uuid_generate = require "util.uuid".generate; local function negotiate_socks5(conn, hash) 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 local function connect_to_usable_streamhost(callback, streamhosts, auth_token) local conn = verse.new(nil, { streamhosts = streamhosts, current_host = 0; }); --Attempt to connect to the next host local function attempt_next_streamhost(event) if event then return callback(nil, event.reason); end -- First connect, or the last connect failed if conn.current_host < #conn.streamhosts then conn.current_host = conn.current_host + 1; conn:debug("Attempting to connect to "..conn.streamhosts[conn.current_host].host..":"..conn.streamhosts[conn.current_host].port.."..."); local ok, err = conn:connect( conn.streamhosts[conn.current_host].host, conn.streamhosts[conn.current_host].port ); if not ok then conn:debug("Error connecting to proxy (%s:%s): %s", conn.streamhosts[conn.current_host].host, conn.streamhosts[conn.current_host].port, err ); else conn:debug("Connecting..."); end negotiate_socks5(conn, auth_token); return true; -- Halt processing of disconnected event end -- All streamhosts tried, none successful conn:unhook("disconnected", attempt_next_streamhost); return callback(nil); -- Let disconnected event fall through to user handlers... end 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); callback(conn.streamhosts[conn.current_host], conn); end, 100); attempt_next_streamhost(); -- Set it in motion return conn; end function verse.plugins.jingle_s5b(stream) stream:hook("ready", function () stream:add_disco_feature(xmlns_s5b); end, 10); local s5b = {}; function s5b:generate_initiate() self.s5b_sid = uuid_generate(); local transport = verse.stanza("transport", { xmlns = xmlns_s5b, mode = "tcp", sid = self.s5b_sid }); local p = 0; for jid, streamhost in pairs(stream.proxy65.available_streamhosts) do p = p + 1; transport:tag("candidate", { jid = jid, host = streamhost.host, port = streamhost.port, cid=jid, priority = p, type = "proxy" }):up(); end stream:debug("Have %d proxies", p) return transport; end function s5b:generate_accept(initiate_transport) local candidates = {}; self.s5b_peer_candidates = candidates; self.s5b_mode = initiate_transport.attr.mode or "tcp"; self.s5b_sid = initiate_transport.attr.sid or self.jingle.sid; -- Import the list of candidates the initiator offered us for candidate in initiate_transport:childtags() do --if candidate.attr.jid == "asterix4@jabber.lagaule.org/Gajim" --and candidate.attr.host == "82.246.25.239" then candidates[candidate.attr.cid] = { type = candidate.attr.type; jid = candidate.attr.jid; host = candidate.attr.host; port = tonumber(candidate.attr.port) or 0; priority = tonumber(candidate.attr.priority) or 0; cid = candidate.attr.cid; }; --end end -- Import our own candidates -- TODO ^ local transport = verse.stanza("transport", { xmlns = xmlns_s5b }); return transport; end function s5b:connect(callback) stream:warn("Connecting!"); local streamhost_array = {}; for cid, streamhost in pairs(self.s5b_peer_candidates or {}) do streamhost_array[#streamhost_array+1] = streamhost; end if #streamhost_array > 0 then self.connecting_peer_candidates = true; local function onconnect(streamhost, conn) self.jingle:send_command("transport-info", verse.stanza("content", { creator = self.creator, name = self.name }) :tag("transport", { xmlns = xmlns_s5b, sid = self.s5b_sid }) :tag("candidate-used", { cid = streamhost.cid })); self.onconnect_callback = callback; self.conn = conn; end local auth_token = sha1(self.s5b_sid..self.peer..stream.jid, true); connect_to_usable_streamhost(onconnect, streamhost_array, auth_token); else stream:warn("Actually, I'm going to wait for my peer to tell me its streamhost..."); self.onconnect_callback = callback; end end function s5b:info_received(jingle_tag) stream:warn("Info received"); local content_tag = jingle_tag:child_with_name("content"); local transport_tag = content_tag:child_with_name("transport"); if transport_tag:get_child("candidate-used") and not self.connecting_peer_candidates then local candidate_used = transport_tag:child_with_name("candidate-used"); if candidate_used then -- Connect straight away to candidate used, we weren't trying any anyway local function onconnect(streamhost, conn) if self.jingle.role == "initiator" then -- More correct would be - "is this a candidate we offered?" -- Activate the stream self.jingle.stream:send_iq(verse.iq({ to = streamhost.jid, type = "set" }) :tag("query", { xmlns = xmlns_bytestreams, sid = self.s5b_sid }) :tag("activate"):text(self.jingle.peer), function (result) if result.attr.type == "result" then self.jingle:send_command("transport-info", verse.stanza("content", content_tag.attr) :tag("transport", { xmlns = xmlns_s5b, sid = self.s5b_sid }) :tag("activated", { cid = candidate_used.attr.cid })); self.conn = conn; self.onconnect_callback(conn); else self.jingle.stream:error("Failed to activate bytestream"); end end); end end -- FIXME: Another assumption that cid==jid, and that it was our candidate self.jingle.stream:debug("CID: %s", self.jingle.stream.proxy65.available_streamhosts[candidate_used.attr.cid]); local streamhost_array = { self.jingle.stream.proxy65.available_streamhosts[candidate_used.attr.cid]; }; local auth_token = sha1(self.s5b_sid..stream.jid..self.peer, true); connect_to_usable_streamhost(onconnect, streamhost_array, auth_token); end elseif transport_tag:get_child("activated") then self.onconnect_callback(self.conn); end end function s5b:disconnect() if self.conn then self.conn:close(); end end function s5b:handle_accepted(jingle_tag) end local s5b_mt = { __index = s5b }; stream:hook("jingle/transport/"..xmlns_s5b, function (jingle) return setmetatable({ role = jingle.role, peer = jingle.peer, stream = jingle.stream, jingle = jingle, }, s5b_mt); end); end