From a14979375b164aa44071dae30521ec15363f3b7a Mon Sep 17 00:00:00 2001 From: Sandro Munda Date: Sun, 3 Jun 2012 19:56:56 +0200 Subject: Added a partial support of the XEP 0065 - Socks5 Bytestreams --- sleekxmpp/plugins/xep_0065/__init__.py | 8 + sleekxmpp/plugins/xep_0065/proxy.py | 300 +++++++++++++++++++++++++++++++++ sleekxmpp/plugins/xep_0065/stanza.py | 38 +++++ 3 files changed, 346 insertions(+) create mode 100644 sleekxmpp/plugins/xep_0065/__init__.py create mode 100644 sleekxmpp/plugins/xep_0065/proxy.py create mode 100644 sleekxmpp/plugins/xep_0065/stanza.py (limited to 'sleekxmpp/plugins/xep_0065') diff --git a/sleekxmpp/plugins/xep_0065/__init__.py b/sleekxmpp/plugins/xep_0065/__init__.py new file mode 100644 index 00000000..8f5dfa6b --- /dev/null +++ b/sleekxmpp/plugins/xep_0065/__init__.py @@ -0,0 +1,8 @@ +""" +""" + +from sleekxmpp.plugins.base import register_plugin +from sleekxmpp.plugins.xep_0065.proxy import xep_0065 + + +register_plugin(xep_0065) diff --git a/sleekxmpp/plugins/xep_0065/proxy.py b/sleekxmpp/plugins/xep_0065/proxy.py new file mode 100644 index 00000000..f33f7840 --- /dev/null +++ b/sleekxmpp/plugins/xep_0065/proxy.py @@ -0,0 +1,300 @@ +import sys +import logging +import struct + +from threading import Thread, Event +from hashlib import sha1 +from select import select +from uuid import uuid4 + +from sleekxmpp.plugins.base import base_plugin +from sleekxmpp import Iq +from sleekxmpp.xmlstream import register_stanza_plugin +from sleekxmpp.xmlstream.handler import Callback +from sleekxmpp.xmlstream.matcher import StanzaPath + +from socks import socksocket, PROXY_TYPE_SOCKS5 + +from stanza import Query, StreamHost, StreamHostUsed + +# Register the sleekxmpp logger +log = logging.getLogger(__name__) + +# Register xep_0065 stanzas +register_stanza_plugin(Iq, Query) +register_stanza_plugin(Query, StreamHost) +register_stanza_plugin(Query, StreamHostUsed) + + +class xep_0065(base_plugin): + """ + XEP-0065 In-Band Bytestreams + """ + + description = "In-Band Bytestreams" + dependencies = set(['xep_0030', ]) + xep = '0065' + + def plugin_init(self): + """ Initializes the xep_0065 plugin and all event callbacks. + """ + + # Shortcuts to access to the xep_0030 plugin. + self.disco = self.xmpp['xep_0030'] + + # Handler for the streamhost stanza. + self.xmpp.registerHandler( + Callback('In-Band Bytestreams', + StanzaPath('iq@type=set/q/streamhost'), + self._handle_streamhost)) + + # Handler for the streamhost-used stanza. + self.xmpp.registerHandler( + Callback('In-Band Bytestreams', + StanzaPath('iq@type=result/q/streamhost-used'), + self._handle_streamhost_used)) + + def handshake(self, to, streamer=None): + """ Starts the handshake to establish the socks5 bytestreams + connection. + """ + + # Discovers the proxy. + self.streamer = streamer or self.discover_proxy() + + # Requester requests network address from the proxy. + streamhost = self.get_network_address(self.streamer) + self.proxy_host = streamhost['q']['streamhost']['host'] + self.proxy_port = streamhost['q']['streamhost']['port'] + + # Generates the SID for this new handshake. + sid = uuid4().hex + + # Requester initiates S5B negotation with Target by sending + # IQ-set that includes the JabberID and network address of + # StreamHost as well as the StreamID (SID) of the proposed + # bytestream. + iq = self.xmpp.Iq(sto=to, stype='set') + iq['q']['sid'] = sid + iq['q']['streamhost']['jid'] = self.streamer + iq['q']['streamhost']['host'] = self.proxy_host + iq['q']['streamhost']['port'] = self.proxy_port + + # Sends the new IQ. + return iq.send() + + def discover_proxy(self): + """ Auto-discovers (using XEP 0030) the available bytestream + proxy on the XMPP server. + + Returns the JID of the proxy. + """ + + # Gets all disco items. + disco_items = self.disco.get_items(self.xmpp.server) + + for item in disco_items['disco_items']['items']: + # For each items, gets the disco info. + disco_info = self.disco.get_info(item[0]) + + # Gets and verifies if the identity is a bytestream proxy. + identities = disco_info['disco_info']['identities'] + for identity in identities: + if identity[0] == 'proxy' and identity[1] == 'bytestreams': + # Returns when the first occurence is found. + return '%s' % disco_info['from'] + + def get_network_address(self, streamer): + iq = self.xmpp.Iq(sto=streamer, stype='get') + iq['q'] + + return iq.send() + + def _handle_streamhost(self, iq): + """ Handles all streamhost stanzas. + """ + + # Registers the streamhost info. + self.streamer = iq['q']['streamhost']['jid'] + self.proxy_host = iq['q']['streamhost']['host'] + self.proxy_port = iq['q']['streamhost']['port'] + + # Sets the SID, the requester and the target. + sid = iq['q']['sid'] + requester = '%s' % iq['from'] + target = '%s' % self.xmpp.boundjid + + # Next the Target attempts to open a standard TCP socket on + # the network address of the Proxy. + self.target_thread = Proxy(sid, requester, target, self.proxy_host, + self.proxy_port, self._handle_on_recv) + self.target_thread.start() + + # Wait until the proxy is connected + self.target_thread.connected.wait() + + # Replies to the incoming iq with a streamhost-used stanza. + res_iq = iq.reply() + res_iq['q']['sid'] = sid + res_iq['q']['streamhost-used']['jid'] = self.streamer + + # Sends the IQ + return res_iq.send() + + def _handle_streamhost_used(self, iq): + """ Handles all streamhost-used stanzas. + """ + + # Sets the requester and the target. + requester = '%s' % self.xmpp.boundjid + target = '%s' % iq['from'] + + # The Requester will establish a connection to the SOCKS5 + # proxy in the same way the Target did. + self.requester_thread = Proxy(iq['q']['sid'], requester, target, + self.proxy_host, self.proxy_port, + self._handle_on_recv) + self.requester_thread.start() + + # Wait until the proxy is connected + self.requester_thread.connected.wait() + + # Requester sends IQ-set to StreamHost requesting that + # StreamHost activate the bytestream associated with the + # StreamID. + self.activate(iq['q']['sid'], target) + + def activate(self, sid, to): + """ IQ-set to StreamHost requesting that StreamHost activate + the bytestream associated with the StreamID. + """ + + # Creates the activate IQ. + act_iq = self.xmpp.Iq(sto=self.streamer, stype='set') + act_iq['q']['sid'] = sid + act_iq['q']['activate'] = to + + # Send the IQ. + act_iq.send() + + def send(self, msg): + """ Sends the msg to the socket. + + msg : The message data. + """ + + if hasattr(self, 'requester_thread'): + self.requester_thread.send(msg) + elif hasattr(self, 'target_thread'): + self.target_thread.send(msg) + + def _handle_on_recv(self, data): + """ A default callback when socket are receiving data. + """ + + log.debug('Received: %s' % data) + + +class Proxy(Thread): + + def __init__(self, sid, requester, target, proxy, proxy_port, + on_recv): + """ Initializes the proxy thread. + + sid : The StreamID. + requester : The JID of the requester. + target : The JID of the target. + proxy_host : The hostname or the IP of the proxy. + proxy_port : The port of the proxy. or + on_recv : A callback called when data are received from the + socket. + """ + + # Initializes the thread. + Thread.__init__(self) + + # Because the xep_0065 plugin uses the proxy_port as string, + # the Proxy class accepts the proxy_port argument as a string + # or an integer. Here, we force to use the port as an integer. + proxy_port = int(proxy_port) + + # Creates a connected event to warn when to proxy is + # connected. + self.connected = Event() + + # Registers the arguments. + self.sid = sid + self.requester = requester + self.target = target + self.proxy = proxy + self.proxy_port = proxy_port + self.on_recv = on_recv + + def run(self): + """ Starts the thread. + """ + + # Creates the socks5 proxy socket + self.s = socksocket() + self.s.setproxy(PROXY_TYPE_SOCKS5, self.proxy, port=self.proxy_port) + + # The hostname MUST be SHA1(SID + Requester JID + Target JID) + # where the output is hexadecimal-encoded (not binary). + digest = sha1() + digest.update(self.sid) # SID + digest.update(self.requester) # Requester JID + digest.update(self.target) # Target JID + + # Computes the digest in hex. + dest = '%s' % digest.hexdigest() + + # The port MUST be 0. + self.s.connect((dest, 0)) + log.info('Connected') + self.connected.set() + + # Listen for data on the socket + self.listen() + + def listen(self): + """ Listen for data on the socket. When receiving data, call + the callback on_recv callable. + """ + + while True: + ins, out, err = select([self.s, ], [], []) + + for s in ins: + data = self.recv_size(self.s) + self.on_recv(data) + + def recv_size(self, the_socket): + #data length is packed into 4 bytes + total_len = 0 + total_data = [] + size = sys.maxint + size_data = sock_data = '' + recv_size = 8192 + + while total_len < size: + sock_data = the_socket.recv(recv_size) + if not total_data: + if len(sock_data) > 4: + size_data += sock_data + size = struct.unpack('>i', size_data[:4])[0] + recv_size = size + if recv_size > 524288: + recv_size = 524288 + total_data.append(size_data[4:]) + else: + size_data += sock_data + else: + total_data.append(sock_data) + total_len = sum([len(i) for i in total_data]) + return ''.join(total_data) + + def send(self, msg): + """ Sends the data over the socket. + """ + + self.s.sendall(msg) diff --git a/sleekxmpp/plugins/xep_0065/stanza.py b/sleekxmpp/plugins/xep_0065/stanza.py new file mode 100644 index 00000000..4d33be98 --- /dev/null +++ b/sleekxmpp/plugins/xep_0065/stanza.py @@ -0,0 +1,38 @@ +from sleekxmpp.xmlstream import ElementBase + +# The protocol namespace defined in the Socks5Bytestream (0065) spec. +namespace = 'http://jabber.org/protocol/bytestreams' + + +class StreamHost(ElementBase): + """ The streamhost xml element. + """ + + namespace = namespace + name = 'streamhost' + plugin_attrib = 'streamhost' + interfaces = set(('host', 'jid', 'port')) + + +class StreamHostUsed(ElementBase): + """ The streamhost-used xml element. + """ + + namespace = 'http://jabber.org/protocol/bytestreams' + name = 'streamhost-used' + plugin_attrib = 'streamhost-used' + interfaces = set(('jid',)) + + +class Query(ElementBase): + """ The query xml element. + """ + + namespace = 'http://jabber.org/protocol/bytestreams' + name = 'query' + plugin_attrib = 'q' + interfaces = set(('sid', 'activate')) + sub_interfaces = set(('activate',)) + + + -- cgit v1.2.3 From 69cffce7dcaea56fa35f9c39299e98eb5576f20d Mon Sep 17 00:00:00 2001 From: Sandro Munda Date: Mon, 4 Jun 2012 07:57:14 +0200 Subject: Used the namespace in all stanzas --- sleekxmpp/plugins/xep_0065/stanza.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sleekxmpp/plugins/xep_0065') diff --git a/sleekxmpp/plugins/xep_0065/stanza.py b/sleekxmpp/plugins/xep_0065/stanza.py index 4d33be98..ef70a368 100644 --- a/sleekxmpp/plugins/xep_0065/stanza.py +++ b/sleekxmpp/plugins/xep_0065/stanza.py @@ -18,7 +18,7 @@ class StreamHostUsed(ElementBase): """ The streamhost-used xml element. """ - namespace = 'http://jabber.org/protocol/bytestreams' + namespace = namespace name = 'streamhost-used' plugin_attrib = 'streamhost-used' interfaces = set(('jid',)) @@ -28,7 +28,7 @@ class Query(ElementBase): """ The query xml element. """ - namespace = 'http://jabber.org/protocol/bytestreams' + namespace = namespace name = 'query' plugin_attrib = 'q' interfaces = set(('sid', 'activate')) -- cgit v1.2.3 From cf24b870b124be3f41c496aa2a99b4c6f626e22f Mon Sep 17 00:00:00 2001 From: Sandro Munda Date: Mon, 4 Jun 2012 08:03:08 +0200 Subject: Registered stanza plugin in the stanza module --- sleekxmpp/plugins/xep_0065/__init__.py | 3 --- sleekxmpp/plugins/xep_0065/proxy.py | 12 ++---------- sleekxmpp/plugins/xep_0065/stanza.py | 9 ++++++--- 3 files changed, 8 insertions(+), 16 deletions(-) (limited to 'sleekxmpp/plugins/xep_0065') diff --git a/sleekxmpp/plugins/xep_0065/__init__.py b/sleekxmpp/plugins/xep_0065/__init__.py index 8f5dfa6b..e1066005 100644 --- a/sleekxmpp/plugins/xep_0065/__init__.py +++ b/sleekxmpp/plugins/xep_0065/__init__.py @@ -1,6 +1,3 @@ -""" -""" - from sleekxmpp.plugins.base import register_plugin from sleekxmpp.plugins.xep_0065.proxy import xep_0065 diff --git a/sleekxmpp/plugins/xep_0065/proxy.py b/sleekxmpp/plugins/xep_0065/proxy.py index f33f7840..d9d629f6 100644 --- a/sleekxmpp/plugins/xep_0065/proxy.py +++ b/sleekxmpp/plugins/xep_0065/proxy.py @@ -8,23 +8,15 @@ from select import select from uuid import uuid4 from sleekxmpp.plugins.base import base_plugin -from sleekxmpp import Iq -from sleekxmpp.xmlstream import register_stanza_plugin from sleekxmpp.xmlstream.handler import Callback from sleekxmpp.xmlstream.matcher import StanzaPath - from socks import socksocket, PROXY_TYPE_SOCKS5 -from stanza import Query, StreamHost, StreamHostUsed +import stanza -# Register the sleekxmpp logger +# Registers the sleekxmpp logger log = logging.getLogger(__name__) -# Register xep_0065 stanzas -register_stanza_plugin(Iq, Query) -register_stanza_plugin(Query, StreamHost) -register_stanza_plugin(Query, StreamHostUsed) - class xep_0065(base_plugin): """ diff --git a/sleekxmpp/plugins/xep_0065/stanza.py b/sleekxmpp/plugins/xep_0065/stanza.py index ef70a368..0990a509 100644 --- a/sleekxmpp/plugins/xep_0065/stanza.py +++ b/sleekxmpp/plugins/xep_0065/stanza.py @@ -1,4 +1,6 @@ -from sleekxmpp.xmlstream import ElementBase +from sleekxmpp import Iq +from sleekxmpp.xmlstream import ElementBase, register_stanza_plugin + # The protocol namespace defined in the Socks5Bytestream (0065) spec. namespace = 'http://jabber.org/protocol/bytestreams' @@ -34,5 +36,6 @@ class Query(ElementBase): interfaces = set(('sid', 'activate')) sub_interfaces = set(('activate',)) - - +register_stanza_plugin(Iq, Query) +register_stanza_plugin(Query, StreamHost) +register_stanza_plugin(Query, StreamHostUsed) -- cgit v1.2.3 From b52d2768b0ff63df3b3006e419477cb9e462c1f9 Mon Sep 17 00:00:00 2001 From: Sandro Munda Date: Mon, 4 Jun 2012 08:07:29 +0200 Subject: Added some comments to the get_network_address method --- sleekxmpp/plugins/xep_0065/proxy.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'sleekxmpp/plugins/xep_0065') diff --git a/sleekxmpp/plugins/xep_0065/proxy.py b/sleekxmpp/plugins/xep_0065/proxy.py index d9d629f6..7d983840 100644 --- a/sleekxmpp/plugins/xep_0065/proxy.py +++ b/sleekxmpp/plugins/xep_0065/proxy.py @@ -97,8 +97,13 @@ class xep_0065(base_plugin): return '%s' % disco_info['from'] def get_network_address(self, streamer): + """ Gets the streamhost information of the proxy. + + streamer : The jid of the proxy. + """ + iq = self.xmpp.Iq(sto=streamer, stype='get') - iq['q'] + iq['q'] # Adds the query eleme to the iq. return iq.send() -- cgit v1.2.3 From 44ee0633f2e55c112ea1f4c48bc39867a2223f55 Mon Sep 17 00:00:00 2001 From: Sandro Munda Date: Mon, 4 Jun 2012 08:27:41 +0200 Subject: Renamed the _handle_on_recv to the on_recv method. Renamed requester_thread and target_thread to proxy. The send method is now simpler. --- sleekxmpp/plugins/xep_0065/proxy.py | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) (limited to 'sleekxmpp/plugins/xep_0065') diff --git a/sleekxmpp/plugins/xep_0065/proxy.py b/sleekxmpp/plugins/xep_0065/proxy.py index 7d983840..eb0b9e8c 100644 --- a/sleekxmpp/plugins/xep_0065/proxy.py +++ b/sleekxmpp/plugins/xep_0065/proxy.py @@ -123,12 +123,12 @@ class xep_0065(base_plugin): # Next the Target attempts to open a standard TCP socket on # the network address of the Proxy. - self.target_thread = Proxy(sid, requester, target, self.proxy_host, - self.proxy_port, self._handle_on_recv) - self.target_thread.start() + self.proxy_thread = Proxy(sid, requester, target, self.proxy_host, + self.proxy_port, self.on_recv) + self.proxy_thread.start() # Wait until the proxy is connected - self.target_thread.connected.wait() + self.proxy_thread.connected.wait() # Replies to the incoming iq with a streamhost-used stanza. res_iq = iq.reply() @@ -148,13 +148,13 @@ class xep_0065(base_plugin): # The Requester will establish a connection to the SOCKS5 # proxy in the same way the Target did. - self.requester_thread = Proxy(iq['q']['sid'], requester, target, + self.proxy_thread = Proxy(iq['q']['sid'], requester, target, self.proxy_host, self.proxy_port, - self._handle_on_recv) - self.requester_thread.start() + self.on_recv) + self.proxy_thread.start() # Wait until the proxy is connected - self.requester_thread.connected.wait() + self.proxy_thread.connected.wait() # Requester sends IQ-set to StreamHost requesting that # StreamHost activate the bytestream associated with the @@ -180,19 +180,21 @@ class xep_0065(base_plugin): msg : The message data. """ - if hasattr(self, 'requester_thread'): - self.requester_thread.send(msg) - elif hasattr(self, 'target_thread'): - self.target_thread.send(msg) + self.proxy_thread.send(msg) - def _handle_on_recv(self, data): - """ A default callback when socket are receiving data. + def on_recv(self, data): + """ A default behavior when socket are receiving data. + + This method should be overriden. """ - log.debug('Received: %s' % data) + log.debug('Received data: %s' % data) class Proxy(Thread): + """ Establishes in a thread a connection between the client and + the server-side Socks5 proxy. + """ def __init__(self, sid, requester, target, proxy, proxy_port, on_recv): @@ -210,6 +212,9 @@ class Proxy(Thread): # Initializes the thread. Thread.__init__(self) + # This thread is a daemon thread. + self.daemon = True + # Because the xep_0065 plugin uses the proxy_port as string, # the Proxy class accepts the proxy_port argument as a string # or an integer. Here, we force to use the port as an integer. @@ -266,7 +271,7 @@ class Proxy(Thread): self.on_recv(data) def recv_size(self, the_socket): - #data length is packed into 4 bytes + # Data length is packed into 4 bytes. total_len = 0 total_data = [] size = sys.maxint -- cgit v1.2.3 From 39505ae1ffbecb3e6edc16b532723f33c8c723f7 Mon Sep 17 00:00:00 2001 From: Sandro Munda Date: Mon, 4 Jun 2012 19:39:48 +0200 Subject: The xep_0065 plugin supports now multiple stream (multiple connected sockets). To send data over a stream, we need to pass the SID in order to retrieve the good proxy thread (and so, the good socket). --- sleekxmpp/plugins/xep_0065/proxy.py | 66 +++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 18 deletions(-) (limited to 'sleekxmpp/plugins/xep_0065') diff --git a/sleekxmpp/plugins/xep_0065/proxy.py b/sleekxmpp/plugins/xep_0065/proxy.py index eb0b9e8c..bcb43cfa 100644 --- a/sleekxmpp/plugins/xep_0065/proxy.py +++ b/sleekxmpp/plugins/xep_0065/proxy.py @@ -27,6 +27,10 @@ class xep_0065(base_plugin): dependencies = set(['xep_0030', ]) xep = '0065' + # A dict contains for each SID, the proxy thread currently + # running. + proxy_threads = {} + def plugin_init(self): """ Initializes the xep_0065 plugin and all event callbacks. """ @@ -127,6 +131,9 @@ class xep_0065(base_plugin): self.proxy_port, self.on_recv) self.proxy_thread.start() + # Registers the new thread in the proxy_thread dict. + self.proxy_threads[sid] = self.proxy_thread + # Wait until the proxy is connected self.proxy_thread.connected.wait() @@ -142,17 +149,20 @@ class xep_0065(base_plugin): """ Handles all streamhost-used stanzas. """ - # Sets the requester and the target. + # Sets the SID, the requester and the target. + sid = iq['q']['sid'] requester = '%s' % self.xmpp.boundjid target = '%s' % iq['from'] # The Requester will establish a connection to the SOCKS5 # proxy in the same way the Target did. - self.proxy_thread = Proxy(iq['q']['sid'], requester, target, - self.proxy_host, self.proxy_port, - self.on_recv) + self.proxy_thread = Proxy(sid, requester, target, self.proxy_host, + self.proxy_port, self.on_recv) self.proxy_thread.start() + # Registers the new thread in the proxy_thread dict. + self.proxy_threads[sid] = self.proxy_thread + # Wait until the proxy is connected self.proxy_thread.connected.wait() @@ -174,21 +184,34 @@ class xep_0065(base_plugin): # Send the IQ. act_iq.send() - def send(self, msg): + def send(self, sid, msg): """ Sends the msg to the socket. + sid : The SID to retrieve the good proxy stored in the + proxy_threads dict msg : The message data. """ - self.proxy_thread.send(msg) - - def on_recv(self, data): - """ A default behavior when socket are receiving data. + proxy = self.proxy_threads.get(sid) + if proxy: + proxy.send(msg) + else: + # TODO: raise an exception. + pass - This method should be overriden. + def on_recv(self, sid, data): + """ Called when receiving data """ - log.debug('Received data: %s' % data) + if not data: + try: + del self.proxy_threads[sid] + except KeyError: + # TODO: internal error, raise an exception ? + pass + else: + log.debug('Received data: %s' % data) + self.xmpp.event('socks_recv', data) class Proxy(Thread): @@ -212,9 +235,6 @@ class Proxy(Thread): # Initializes the thread. Thread.__init__(self) - # This thread is a daemon thread. - self.daemon = True - # Because the xep_0065 plugin uses the proxy_port as string, # the Proxy class accepts the proxy_port argument as a string # or an integer. Here, we force to use the port as an integer. @@ -252,26 +272,33 @@ class Proxy(Thread): # The port MUST be 0. self.s.connect((dest, 0)) - log.info('Connected') + log.info('Socket connected.') self.connected.set() # Listen for data on the socket self.listen() + # Listen returns when the socket must be closed. + self.s.close() + log.info('Socket closed.') + def listen(self): """ Listen for data on the socket. When receiving data, call the callback on_recv callable. """ - while True: + socket_open = True + while socket_open: ins, out, err = select([self.s, ], [], []) for s in ins: data = self.recv_size(self.s) - self.on_recv(data) + if not data: + socket_open = False + + self.on_recv(self.sid, data) def recv_size(self, the_socket): - # Data length is packed into 4 bytes. total_len = 0 total_data = [] size = sys.maxint @@ -280,6 +307,9 @@ class Proxy(Thread): while total_len < size: sock_data = the_socket.recv(recv_size) + if not sock_data: + return ''.join(total_data) + if not total_data: if len(sock_data) > 4: size_data += sock_data -- cgit v1.2.3 From 2f388576817b80acf07443fed2369e7d110fe1e8 Mon Sep 17 00:00:00 2001 From: Sandro Munda Date: Tue, 5 Jun 2012 08:33:21 +0200 Subject: Changed the description of the xep_0065 plugin --- sleekxmpp/plugins/xep_0065/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sleekxmpp/plugins/xep_0065') diff --git a/sleekxmpp/plugins/xep_0065/proxy.py b/sleekxmpp/plugins/xep_0065/proxy.py index bcb43cfa..865c2f90 100644 --- a/sleekxmpp/plugins/xep_0065/proxy.py +++ b/sleekxmpp/plugins/xep_0065/proxy.py @@ -23,7 +23,7 @@ class xep_0065(base_plugin): XEP-0065 In-Band Bytestreams """ - description = "In-Band Bytestreams" + description = "Socks5 Bytestreams" dependencies = set(['xep_0030', ]) xep = '0065' -- cgit v1.2.3 From 2cd936318ddf7a0b3e096f7070ec83a16ddb9e89 Mon Sep 17 00:00:00 2001 From: Sandro Munda Date: Tue, 5 Jun 2012 08:33:47 +0200 Subject: Improved the close of the proxy thread (and the socket) in the xep_0065 plugin. --- sleekxmpp/plugins/xep_0065/proxy.py | 63 +++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 27 deletions(-) (limited to 'sleekxmpp/plugins/xep_0065') diff --git a/sleekxmpp/plugins/xep_0065/proxy.py b/sleekxmpp/plugins/xep_0065/proxy.py index 865c2f90..86598df3 100644 --- a/sleekxmpp/plugins/xep_0065/proxy.py +++ b/sleekxmpp/plugins/xep_0065/proxy.py @@ -184,33 +184,39 @@ class xep_0065(base_plugin): # Send the IQ. act_iq.send() - def send(self, sid, msg): - """ Sends the msg to the socket. + def deactivate(self, sid): + """ Closes the Proxy thread associated to this SID. + """ + + proxy = self.proxy_threads.get(sid) + if proxy: + proxy.s.close() + del self.proxy_threads[sid] + + def close(self): + """ Closes all Proxy threads. + """ + + for sid, proxy in self.proxy_threads.items(): + proxy.s.close() + del self.proxy_threads[sid] - sid : The SID to retrieve the good proxy stored in the - proxy_threads dict - msg : The message data. + def send(self, sid, data): + """ Sends the data over the Proxy socket associated to the + SID. """ proxy = self.proxy_threads.get(sid) if proxy: - proxy.send(msg) - else: - # TODO: raise an exception. - pass + proxy.s.sendall(data) def on_recv(self, sid, data): - """ Called when receiving data + """ Calls when data is recv from the Proxy socket associated + to the SID. """ - if not data: - try: - del self.proxy_threads[sid] - except KeyError: - # TODO: internal error, raise an exception ? - pass - else: - log.debug('Received data: %s' % data) + proxy = self.proxy_threads.get(sid) + if proxy: self.xmpp.event('socks_recv', data) @@ -275,10 +281,10 @@ class Proxy(Thread): log.info('Socket connected.') self.connected.set() - # Listen for data on the socket + # Blocks until the socket need to be closed. self.listen() - # Listen returns when the socket must be closed. + # Closes the socket. self.s.close() log.info('Socket closed.') @@ -289,7 +295,16 @@ class Proxy(Thread): socket_open = True while socket_open: - ins, out, err = select([self.s, ], [], []) + ins = [] + try: + # Wait any read available data on socket. Timeout + # after 5 secs. + ins, out, err = select([self.s, ], [], [], 5) + except Exception, e: + # There's an error with the socket (maybe the socket + # has been closed and the file descriptor is bad). + log.debug('Socket error: %s' % e) + break for s in ins: data = self.recv_size(self.s) @@ -324,9 +339,3 @@ class Proxy(Thread): total_data.append(sock_data) total_len = sum([len(i) for i in total_data]) return ''.join(total_data) - - def send(self, msg): - """ Sends the data over the socket. - """ - - self.s.sendall(msg) -- cgit v1.2.3 From c59a6d0f51112b1bc35f9d492342dea63bfce6e5 Mon Sep 17 00:00:00 2001 From: Sandro Munda Date: Thu, 7 Jun 2012 18:37:42 +0200 Subject: Sent a socks_closed when the socket is closed in the xep_0065 plugin. --- sleekxmpp/plugins/xep_0065/proxy.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sleekxmpp/plugins/xep_0065') diff --git a/sleekxmpp/plugins/xep_0065/proxy.py b/sleekxmpp/plugins/xep_0065/proxy.py index 86598df3..89f37399 100644 --- a/sleekxmpp/plugins/xep_0065/proxy.py +++ b/sleekxmpp/plugins/xep_0065/proxy.py @@ -217,7 +217,10 @@ class xep_0065(base_plugin): proxy = self.proxy_threads.get(sid) if proxy: - self.xmpp.event('socks_recv', data) + if not data: + self.xmpp.event('socks_closed', sid) + else: + self.xmpp.event('socks_recv', data) class Proxy(Thread): -- cgit v1.2.3 From dcdf5dcd098ee8033ca9036af7e2f6ab8ddba620 Mon Sep 17 00:00:00 2001 From: Sandro Munda Date: Thu, 7 Jun 2012 19:02:09 +0200 Subject: Added the Socksipy module in the thirdparty of SleekXMPP. Updated the LICENSE file with the license of the Socksipy module (New-BSD). --- sleekxmpp/plugins/xep_0065/proxy.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sleekxmpp/plugins/xep_0065') diff --git a/sleekxmpp/plugins/xep_0065/proxy.py b/sleekxmpp/plugins/xep_0065/proxy.py index 89f37399..f7b259f2 100644 --- a/sleekxmpp/plugins/xep_0065/proxy.py +++ b/sleekxmpp/plugins/xep_0065/proxy.py @@ -7,12 +7,12 @@ from hashlib import sha1 from select import select from uuid import uuid4 +import stanza + from sleekxmpp.plugins.base import base_plugin from sleekxmpp.xmlstream.handler import Callback from sleekxmpp.xmlstream.matcher import StanzaPath -from socks import socksocket, PROXY_TYPE_SOCKS5 - -import stanza +from sleekxmpp.thirdparty.socks import socksocket, PROXY_TYPE_SOCKS5 # Registers the sleekxmpp logger log = logging.getLogger(__name__) -- cgit v1.2.3 From ae01f1071ab4634d40b334e956298c2004904815 Mon Sep 17 00:00:00 2001 From: Sandro Munda Date: Thu, 7 Jun 2012 19:04:24 +0200 Subject: Fixed the callback names of the xep_0065: In-Band bytestreams -> Socks5 bytestreams --- sleekxmpp/plugins/xep_0065/proxy.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sleekxmpp/plugins/xep_0065') diff --git a/sleekxmpp/plugins/xep_0065/proxy.py b/sleekxmpp/plugins/xep_0065/proxy.py index f7b259f2..c1df23b4 100644 --- a/sleekxmpp/plugins/xep_0065/proxy.py +++ b/sleekxmpp/plugins/xep_0065/proxy.py @@ -20,7 +20,7 @@ log = logging.getLogger(__name__) class xep_0065(base_plugin): """ - XEP-0065 In-Band Bytestreams + XEP-0065 Socks5 Bytestreams """ description = "Socks5 Bytestreams" @@ -40,13 +40,13 @@ class xep_0065(base_plugin): # Handler for the streamhost stanza. self.xmpp.registerHandler( - Callback('In-Band Bytestreams', + Callback('Socks5 Bytestreams', StanzaPath('iq@type=set/q/streamhost'), self._handle_streamhost)) # Handler for the streamhost-used stanza. self.xmpp.registerHandler( - Callback('In-Band Bytestreams', + Callback('Socks5 Bytestreams', StanzaPath('iq@type=result/q/streamhost-used'), self._handle_streamhost_used)) -- cgit v1.2.3 From 26147f5ae0f2cf2e59c25ce90ff71653125e6b69 Mon Sep 17 00:00:00 2001 From: Sandro Munda Date: Thu, 7 Jun 2012 19:08:20 +0200 Subject: Added a top level field to the xep_0065 class: name = 'xep_0065' --- sleekxmpp/plugins/xep_0065/proxy.py | 1 + 1 file changed, 1 insertion(+) (limited to 'sleekxmpp/plugins/xep_0065') diff --git a/sleekxmpp/plugins/xep_0065/proxy.py b/sleekxmpp/plugins/xep_0065/proxy.py index c1df23b4..88138b85 100644 --- a/sleekxmpp/plugins/xep_0065/proxy.py +++ b/sleekxmpp/plugins/xep_0065/proxy.py @@ -26,6 +26,7 @@ class xep_0065(base_plugin): description = "Socks5 Bytestreams" dependencies = set(['xep_0030', ]) xep = '0065' + name = 'xep_0065' # A dict contains for each SID, the proxy thread currently # running. -- cgit v1.2.3 From 289b0523387e3c6dc7bde1161736aa791d6bcc99 Mon Sep 17 00:00:00 2001 From: Sandro Munda Date: Thu, 7 Jun 2012 19:14:37 +0200 Subject: Renamed Query to Socks5 in the xep_0065. Renamed the 'q' plugin_attrib of the Socks5 stanza to 'socks'. --- sleekxmpp/plugins/xep_0065/proxy.py | 38 ++++++++++++++++++------------------ sleekxmpp/plugins/xep_0065/stanza.py | 10 +++++----- 2 files changed, 24 insertions(+), 24 deletions(-) (limited to 'sleekxmpp/plugins/xep_0065') diff --git a/sleekxmpp/plugins/xep_0065/proxy.py b/sleekxmpp/plugins/xep_0065/proxy.py index 88138b85..f950b6aa 100644 --- a/sleekxmpp/plugins/xep_0065/proxy.py +++ b/sleekxmpp/plugins/xep_0065/proxy.py @@ -42,13 +42,13 @@ class xep_0065(base_plugin): # Handler for the streamhost stanza. self.xmpp.registerHandler( Callback('Socks5 Bytestreams', - StanzaPath('iq@type=set/q/streamhost'), + StanzaPath('iq@type=set/socks/streamhost'), self._handle_streamhost)) # Handler for the streamhost-used stanza. self.xmpp.registerHandler( Callback('Socks5 Bytestreams', - StanzaPath('iq@type=result/q/streamhost-used'), + StanzaPath('iq@type=result/socks/streamhost-used'), self._handle_streamhost_used)) def handshake(self, to, streamer=None): @@ -61,8 +61,8 @@ class xep_0065(base_plugin): # Requester requests network address from the proxy. streamhost = self.get_network_address(self.streamer) - self.proxy_host = streamhost['q']['streamhost']['host'] - self.proxy_port = streamhost['q']['streamhost']['port'] + self.proxy_host = streamhost['socks']['streamhost']['host'] + self.proxy_port = streamhost['socks']['streamhost']['port'] # Generates the SID for this new handshake. sid = uuid4().hex @@ -72,10 +72,10 @@ class xep_0065(base_plugin): # StreamHost as well as the StreamID (SID) of the proposed # bytestream. iq = self.xmpp.Iq(sto=to, stype='set') - iq['q']['sid'] = sid - iq['q']['streamhost']['jid'] = self.streamer - iq['q']['streamhost']['host'] = self.proxy_host - iq['q']['streamhost']['port'] = self.proxy_port + iq['socks']['sid'] = sid + iq['socks']['streamhost']['jid'] = self.streamer + iq['socks']['streamhost']['host'] = self.proxy_host + iq['socks']['streamhost']['port'] = self.proxy_port # Sends the new IQ. return iq.send() @@ -108,7 +108,7 @@ class xep_0065(base_plugin): """ iq = self.xmpp.Iq(sto=streamer, stype='get') - iq['q'] # Adds the query eleme to the iq. + iq['socks'] # Adds the query eleme to the iq. return iq.send() @@ -117,12 +117,12 @@ class xep_0065(base_plugin): """ # Registers the streamhost info. - self.streamer = iq['q']['streamhost']['jid'] - self.proxy_host = iq['q']['streamhost']['host'] - self.proxy_port = iq['q']['streamhost']['port'] + self.streamer = iq['socks']['streamhost']['jid'] + self.proxy_host = iq['socks']['streamhost']['host'] + self.proxy_port = iq['socks']['streamhost']['port'] # Sets the SID, the requester and the target. - sid = iq['q']['sid'] + sid = iq['socks']['sid'] requester = '%s' % iq['from'] target = '%s' % self.xmpp.boundjid @@ -140,8 +140,8 @@ class xep_0065(base_plugin): # Replies to the incoming iq with a streamhost-used stanza. res_iq = iq.reply() - res_iq['q']['sid'] = sid - res_iq['q']['streamhost-used']['jid'] = self.streamer + res_iq['socks']['sid'] = sid + res_iq['socks']['streamhost-used']['jid'] = self.streamer # Sends the IQ return res_iq.send() @@ -151,7 +151,7 @@ class xep_0065(base_plugin): """ # Sets the SID, the requester and the target. - sid = iq['q']['sid'] + sid = iq['socks']['sid'] requester = '%s' % self.xmpp.boundjid target = '%s' % iq['from'] @@ -170,7 +170,7 @@ class xep_0065(base_plugin): # Requester sends IQ-set to StreamHost requesting that # StreamHost activate the bytestream associated with the # StreamID. - self.activate(iq['q']['sid'], target) + self.activate(iq['socks']['sid'], target) def activate(self, sid, to): """ IQ-set to StreamHost requesting that StreamHost activate @@ -179,8 +179,8 @@ class xep_0065(base_plugin): # Creates the activate IQ. act_iq = self.xmpp.Iq(sto=self.streamer, stype='set') - act_iq['q']['sid'] = sid - act_iq['q']['activate'] = to + act_iq['socks']['sid'] = sid + act_iq['socks']['activate'] = to # Send the IQ. act_iq.send() diff --git a/sleekxmpp/plugins/xep_0065/stanza.py b/sleekxmpp/plugins/xep_0065/stanza.py index 0990a509..ae57aba8 100644 --- a/sleekxmpp/plugins/xep_0065/stanza.py +++ b/sleekxmpp/plugins/xep_0065/stanza.py @@ -26,16 +26,16 @@ class StreamHostUsed(ElementBase): interfaces = set(('jid',)) -class Query(ElementBase): +class Socks5(ElementBase): """ The query xml element. """ namespace = namespace name = 'query' - plugin_attrib = 'q' + plugin_attrib = 'socks' interfaces = set(('sid', 'activate')) sub_interfaces = set(('activate',)) -register_stanza_plugin(Iq, Query) -register_stanza_plugin(Query, StreamHost) -register_stanza_plugin(Query, StreamHostUsed) +register_stanza_plugin(Iq, Socks5) +register_stanza_plugin(Socks5, StreamHost) +register_stanza_plugin(Socks5, StreamHostUsed) -- cgit v1.2.3 From 1851ab6f5fe3548eccbc118bdb71340ee92dd9da Mon Sep 17 00:00:00 2001 From: Sandro Munda Date: Thu, 7 Jun 2012 19:24:23 +0200 Subject: Added the SID in the socks_recv xmpp event in the xep_0065 plugin. --- sleekxmpp/plugins/xep_0065/proxy.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'sleekxmpp/plugins/xep_0065') diff --git a/sleekxmpp/plugins/xep_0065/proxy.py b/sleekxmpp/plugins/xep_0065/proxy.py index f950b6aa..60b7d6ed 100644 --- a/sleekxmpp/plugins/xep_0065/proxy.py +++ b/sleekxmpp/plugins/xep_0065/proxy.py @@ -214,6 +214,12 @@ class xep_0065(base_plugin): def on_recv(self, sid, data): """ Calls when data is recv from the Proxy socket associated to the SID. + + Triggers a socks_closed event if the socket is closed. The sid + is passed to this event. + + Triggers a socks_recv event if there's available data. A dict + that contains the sid and the data is passed to this event. """ proxy = self.proxy_threads.get(sid) @@ -221,7 +227,7 @@ class xep_0065(base_plugin): if not data: self.xmpp.event('socks_closed', sid) else: - self.xmpp.event('socks_recv', data) + self.xmpp.event('socks_recv', {'sid': sid, 'data': data}) class Proxy(Thread): -- cgit v1.2.3 From 8def3758e4e849f25001e1e76616fcc3836bd1c2 Mon Sep 17 00:00:00 2001 From: Sandro Munda Date: Thu, 7 Jun 2012 19:36:25 +0200 Subject: Added the get_socket(sid) method to the xep_0065 plugin to retrieve the socket of the Proxy thread. --- sleekxmpp/plugins/xep_0065/proxy.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'sleekxmpp/plugins/xep_0065') diff --git a/sleekxmpp/plugins/xep_0065/proxy.py b/sleekxmpp/plugins/xep_0065/proxy.py index 60b7d6ed..4fdd2ad8 100644 --- a/sleekxmpp/plugins/xep_0065/proxy.py +++ b/sleekxmpp/plugins/xep_0065/proxy.py @@ -51,6 +51,14 @@ class xep_0065(base_plugin): StanzaPath('iq@type=result/socks/streamhost-used'), self._handle_streamhost_used)) + def get_socket(self, sid): + """ Returns the socket associated to the SID. + """ + + proxy = self.proxy_threads.get(sid) + if proxy: + return proxy.s + def handshake(self, to, streamer=None): """ Starts the handshake to establish the socks5 bytestreams connection. -- cgit v1.2.3