Compare commits
	
		
			89 Commits
		
	
	
		
			slix-1.0
			...
			hildjj-dev
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 48def71d0c | ||
|   | c8c20fff71 | ||
|   | 75a18b5ffe | ||
|   | 06a690a259 | ||
|   | 14c9e9a9cc | ||
|   | 931d49560a | ||
|   | 3655827ef2 | ||
|   | 12e0e1a16b | ||
|   | 0d448b8221 | ||
|   | 77f2a339e1 | ||
|   | 7c485c6a8b | ||
|   | fc07e23ff8 | ||
|   | 84a2fc382b | ||
|   | 44e7585bf8 | ||
|   | a2c60a4911 | ||
|   | 73ce9a5ecc | ||
|   | d385b9e708 | ||
|   | 67147570e9 | ||
|   | df9ac58d05 | ||
|   | 19a78f63f4 | ||
|   | e20610ab80 | ||
|   | f52a10b061 | ||
|   | 7d382a2bfd | ||
|   | 09bec1c4fe | ||
|   | ff28b0a005 | ||
|   | a249f8736a | ||
|   | f09adf0014 | ||
|   | 04dc68f5f6 | ||
|   | 5c25208fb5 | ||
|   | 962dfad216 | ||
|   | 14aa831169 | ||
|   | 75d904ed01 | ||
|   | f81d5e4bd6 | ||
|   | 2f65fdbc76 | ||
|   | 2f4149c7d0 | ||
|   | fb4275648c | ||
|   | 06a9d9fc30 | ||
|   | 44ce01a70b | ||
|   | c2189b4ecd | ||
|   | c9b2cf6043 | ||
|   | 16ec0f151a | ||
|   | c42f1ad4c7 | ||
|   | a3ec1af205 | ||
|   | 2e580304f9 | ||
|   | 5492e9028d | ||
|   | 060c9ab679 | ||
|   | 78f0325398 | ||
|   | 1efe049959 | ||
|   | 2393148908 | ||
|   | c7594b3ef0 | ||
|   | b210870f48 | ||
|   | 5d6019a962 | ||
|   | eb5df1aa37 | ||
|   | 546066d677 | ||
|   | 3234596974 | ||
|   | 5820d49cd4 | ||
|   | 1ab66e5767 | ||
|   | aab2682f9a | ||
|   | 55d332bcc8 | ||
|   | f89df6e70c | ||
|   | 250d28e870 | ||
|   | 19f65c8510 | ||
|   | f70b49882f | ||
|   | a7b092a305 | ||
|   | daa73a3f3c | ||
|   | 0b51afe87a | ||
|   | 2b298766c9 | ||
|   | 10664d723b | ||
|   | c012208a8f | ||
|   | 0953896d2d | ||
|   | cf9e89d0ae | ||
|   | 48dd01b0bb | ||
|   | 7247efe055 | ||
|   | 8def3758e4 | ||
|   | 1851ab6f5f | ||
|   | 289b052338 | ||
|   | 26147f5ae0 | ||
|   | ae01f1071a | ||
|   | dcdf5dcd09 | ||
|   | c59a6d0f51 | ||
|   | 2cd936318d | ||
|   | 2f38857681 | ||
|   | 39505ae1ff | ||
|   | 44ee0633f2 | ||
|   | b52d2768b0 | ||
|   | cf24b870b1 | ||
|   | 69cffce7dc | ||
|   | a14979375b | ||
|   | 40ef4a16b1 | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -7,3 +7,4 @@ docs/_build/ | ||||
| .tox/ | ||||
| .coverage | ||||
| sleekxmpp.egg-info/ | ||||
| .ropeproject/ | ||||
|   | ||||
							
								
								
									
										29
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								LICENSE
									
									
									
									
									
								
							| @@ -69,8 +69,8 @@ modification, are permitted provided that the following conditions are met: | ||||
|     * Redistributions in binary form must reproduce the above copyright | ||||
|       notice, this list of conditions and the following disclaimer in the | ||||
|       documentation and/or other materials provided with the distribution. | ||||
|     * Neither the name of Red Innovation nor the names of its contributors  | ||||
|       may be used to endorse or promote products derived from this software  | ||||
|     * Neither the name of Red Innovation nor the names of its contributors | ||||
|       may be used to endorse or promote products derived from this software | ||||
|       without specific prior written permission. | ||||
|  | ||||
| THIS SOFTWARE IS PROVIDED BY RED INNOVATION ``AS IS'' AND ANY | ||||
| @@ -167,3 +167,28 @@ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||||
| LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE | ||||
| OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | ||||
| ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| socksipy: A Python SOCKS client module. | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| Copyright 2006 Dan-Haim. All rights reserved. | ||||
|  | ||||
| Redistribution and use in source and binary forms, with or without modification, | ||||
| are permitted provided that the following conditions are met: | ||||
| 1. Redistributions of source code must retain the above copyright notice, this | ||||
|    list of conditions and the following disclaimer. | ||||
| 2. Redistributions in binary form must reproduce the above copyright notice, | ||||
|    this list of conditions and the following disclaimer in the documentation | ||||
|    and/or other materials provided with the distribution. | ||||
| 3. Neither the name of Dan Haim nor the names of his contributors may be used | ||||
|    to endorse or promote products derived from this software without specific | ||||
|    prior written permission. | ||||
|  | ||||
| THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED | ||||
| WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||||
| MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||||
| EVENT SHALL DAN HAIM OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||
| INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA | ||||
| OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||||
| LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | ||||
| OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE. | ||||
|   | ||||
							
								
								
									
										1
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								setup.py
									
									
									
									
									
								
							| @@ -73,6 +73,7 @@ packages     = [ 'sleekxmpp', | ||||
|                  'sleekxmpp/plugins/xep_0059', | ||||
|                  'sleekxmpp/plugins/xep_0060', | ||||
|                  'sleekxmpp/plugins/xep_0060/stanza', | ||||
|                  'sleekxmpp/plugins/xep_0065', | ||||
|                  'sleekxmpp/plugins/xep_0066', | ||||
|                  'sleekxmpp/plugins/xep_0077', | ||||
|                  'sleekxmpp/plugins/xep_0078', | ||||
|   | ||||
| @@ -69,6 +69,20 @@ JID_CACHE = OrderedDict() | ||||
| JID_CACHE_LOCK = threading.Lock() | ||||
| JID_CACHE_MAX_SIZE = 1024 | ||||
|  | ||||
| def _cache(key, parts, locked): | ||||
|     JID_CACHE[key] = (parts, locked) | ||||
|     if len(JID_CACHE) > JID_CACHE_MAX_SIZE: | ||||
|         with JID_CACHE_LOCK: | ||||
|             while len(JID_CACHE) > JID_CACHE_MAX_SIZE: | ||||
|                 found = None | ||||
|                 for key, item in JID_CACHE.iteritems(): | ||||
|                     if not item[1]: # if not locked | ||||
|                         found = key | ||||
|                         break | ||||
|                 if not found: # more than MAX_SIZE locked | ||||
|                     # warn? | ||||
|                     break | ||||
|                 del JID_CACHE[found] | ||||
|  | ||||
| # pylint: disable=c0103 | ||||
| #: The nodeprep profile of stringprep used to validate the local, | ||||
| @@ -418,19 +432,29 @@ class JID(object): | ||||
|  | ||||
|     # pylint: disable=W0212 | ||||
|     def __init__(self, jid=None, **kwargs): | ||||
|         jid_data = (jid, kwargs.get('local', None), | ||||
|                          kwargs.get('domain', None), | ||||
|                          kwargs.get('resource', None)) | ||||
|  | ||||
|         locked = kwargs.get('cache_lock', False) | ||||
|         in_local = kwargs.get('local', None) | ||||
|         in_domain = kwargs.get('domain', None) | ||||
|         in_resource = kwargs.get('resource', None) | ||||
|         parts = None | ||||
|         if in_local or in_domain or in_resource: | ||||
|             parts = (in_local, in_domain, in_resource) | ||||
|  | ||||
|         if jid_data in JID_CACHE: | ||||
|             parsed_jid, locked = JID_CACHE[jid_data] | ||||
|             self._jid = parsed_jid | ||||
|         else: | ||||
|             if jid is None: | ||||
|                 jid = '' | ||||
|  | ||||
|         # only check cache if there is a jid string, or parts, not if there | ||||
|         # are both | ||||
|         self._jid = None | ||||
|         key = None | ||||
|         if (jid is not None) and (parts is None): | ||||
|             if isinstance(jid, JID): | ||||
|                 # it's already good to go, and there are no additions | ||||
|                 self._jid = jid._jid | ||||
|                 return | ||||
|             key = jid | ||||
|             self._jid, locked = JID_CACHE.get(jid, (None, locked)) | ||||
|         elif jid is None and parts is not None: | ||||
|             key = parts | ||||
|             self._jid, locked = JID_CACHE.get(parts, (None, locked)) | ||||
|         if not self._jid: | ||||
|             if not jid: | ||||
|                 parsed_jid = (None, None, None) | ||||
|             elif not isinstance(jid, JID): | ||||
| @@ -440,27 +464,16 @@ class JID(object): | ||||
|  | ||||
|             local, domain, resource = parsed_jid | ||||
|  | ||||
|             local = kwargs.get('local', local) | ||||
|             domain = kwargs.get('domain', domain) | ||||
|             resource = kwargs.get('resource', resource) | ||||
|  | ||||
|             if 'local' in kwargs: | ||||
|                 local = _escape_node(local) | ||||
|                 local = _escape_node(in_local) | ||||
|             if 'domain' in kwargs: | ||||
|                 domain = _validate_domain(domain) | ||||
|                 domain = _validate_domain(in_domain) | ||||
|             if 'resource' in kwargs: | ||||
|                 resource = _validate_resource(resource) | ||||
|                 resource = _validate_resource(in_resource) | ||||
|  | ||||
|             self._jid = (local, domain, resource) | ||||
|  | ||||
|         JID_CACHE[jid_data] = (self._jid, locked) | ||||
|         if len(JID_CACHE) > JID_CACHE_MAX_SIZE: | ||||
|             with JID_CACHE_LOCK: | ||||
|                 key, item = JID_CACHE.popitem(False) | ||||
|                 if item[1]: | ||||
|                     # Need to reinsert locked JIDs | ||||
|                     JID_CACHE[key] = item | ||||
|  | ||||
|             if key: | ||||
|                 _cache(key, self._jid, locked) | ||||
|  | ||||
|     def unescape(self): | ||||
|         """Return an unescaped JID object. | ||||
|   | ||||
| @@ -30,6 +30,7 @@ __all__ = [ | ||||
|     'xep_0054',  # vcard-temp | ||||
|     'xep_0059',  # Result Set Management | ||||
|     'xep_0060',  # Pubsub (Client) | ||||
|     'xep_0065',  # SOCKS5 Bytestreams | ||||
|     'xep_0066',  # Out of Band Data | ||||
|     'xep_0077',  # In-Band Registration | ||||
| #   'xep_0078',  # Non-SASL auth. Don't automatically load | ||||
|   | ||||
| @@ -324,6 +324,8 @@ class XEP_0030(BasePlugin): | ||||
|             callback -- Optional callback to execute when a reply is | ||||
|                         received instead of blocking and waiting for | ||||
|                         the reply. | ||||
|             timeout_callback -- Optional callback to execute when no result  | ||||
|                         has been received in timeout seconds. | ||||
|         """ | ||||
|         if local is None: | ||||
|             if jid is not None and not isinstance(jid, JID): | ||||
| @@ -364,7 +366,8 @@ class XEP_0030(BasePlugin): | ||||
|         iq['disco_info']['node'] = node if node else '' | ||||
|         return iq.send(timeout=kwargs.get('timeout', None), | ||||
|                        block=kwargs.get('block', True), | ||||
|                        callback=kwargs.get('callback', None)) | ||||
|                        callback=kwargs.get('callback', None), | ||||
|                        timeout_callback=kwargs.get('timeout_callback', None)) | ||||
|  | ||||
|     def set_info(self, jid=None, node=None, info=None): | ||||
|         """ | ||||
| @@ -405,6 +408,8 @@ class XEP_0030(BasePlugin): | ||||
|             iterator -- If True, return a result set iterator using | ||||
|                         the XEP-0059 plugin, if the plugin is loaded. | ||||
|                         Otherwise the parameter is ignored. | ||||
|             timeout_callback -- Optional callback to execute when no result  | ||||
|                         has been received in timeout seconds. | ||||
|         """ | ||||
|         if local or local is None and jid is None: | ||||
|             items = self.api['get_items'](jid, node, | ||||
| @@ -423,7 +428,8 @@ class XEP_0030(BasePlugin): | ||||
|         else: | ||||
|             return iq.send(timeout=kwargs.get('timeout', None), | ||||
|                            block=kwargs.get('block', True), | ||||
|                            callback=kwargs.get('callback', None)) | ||||
|                            callback=kwargs.get('callback', None), | ||||
|                            timeout_callback=kwargs.get('timeout_callback', None)) | ||||
|  | ||||
|     def set_items(self, jid=None, node=None, **kwargs): | ||||
|         """ | ||||
|   | ||||
							
								
								
									
										5
									
								
								sleekxmpp/plugins/xep_0065/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								sleekxmpp/plugins/xep_0065/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| from sleekxmpp.plugins.base import register_plugin | ||||
| from sleekxmpp.plugins.xep_0065.proxy import XEP_0065 | ||||
|  | ||||
|  | ||||
| register_plugin(XEP_0065) | ||||
							
								
								
									
										359
									
								
								sleekxmpp/plugins/xep_0065/proxy.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										359
									
								
								sleekxmpp/plugins/xep_0065/proxy.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,359 @@ | ||||
| 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.xep_0065 import stanza | ||||
|  | ||||
| from sleekxmpp.plugins.base import base_plugin | ||||
| from sleekxmpp.xmlstream.handler import Callback | ||||
| from sleekxmpp.xmlstream.matcher import StanzaPath | ||||
| from sleekxmpp.thirdparty.socks import socksocket, PROXY_TYPE_SOCKS5 | ||||
|  | ||||
| # Registers the sleekxmpp logger | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class XEP_0065(base_plugin): | ||||
|     """ | ||||
|     XEP-0065 Socks5 Bytestreams | ||||
|     """ | ||||
|  | ||||
|     description = "Socks5 Bytestreams" | ||||
|     dependencies = set(['xep_0030', ]) | ||||
|     xep = '0065' | ||||
|     name = '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. | ||||
|         """ | ||||
|  | ||||
|         # Shortcuts to access to the xep_0030 plugin. | ||||
|         self.disco = self.xmpp['xep_0030'] | ||||
|  | ||||
|         # Handler for the streamhost stanza. | ||||
|         self.xmpp.registerHandler( | ||||
|             Callback('Socks5 Bytestreams', | ||||
|                      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/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. | ||||
|         """ | ||||
|  | ||||
|         # 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['socks']['streamhost']['host'] | ||||
|         self.proxy_port = streamhost['socks']['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['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() | ||||
|  | ||||
|     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): | ||||
|         """ Gets the streamhost information of the proxy. | ||||
|  | ||||
|         streamer : The jid of the proxy. | ||||
|         """ | ||||
|  | ||||
|         iq = self.xmpp.Iq(sto=streamer, stype='get') | ||||
|         iq['socks']  # Adds the query eleme to the iq. | ||||
|  | ||||
|         return iq.send() | ||||
|  | ||||
|     def _handle_streamhost(self, iq): | ||||
|         """ Handles all streamhost stanzas. | ||||
|         """ | ||||
|  | ||||
|         # Registers the streamhost info. | ||||
|         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['socks']['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.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() | ||||
|  | ||||
|         # Replies to the incoming iq with a streamhost-used stanza. | ||||
|         res_iq = iq.reply() | ||||
|         res_iq['socks']['sid'] = sid | ||||
|         res_iq['socks']['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 SID, the requester and the target. | ||||
|         sid = iq['socks']['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(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() | ||||
|  | ||||
|         # Requester sends IQ-set to StreamHost requesting that | ||||
|         # StreamHost activate the bytestream associated with the | ||||
|         # StreamID. | ||||
|         self.activate(iq['socks']['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['socks']['sid'] = sid | ||||
|         act_iq['socks']['activate'] = to | ||||
|  | ||||
|         # Send the IQ. | ||||
|         act_iq.send() | ||||
|  | ||||
|     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] | ||||
|  | ||||
|     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.s.sendall(data) | ||||
|  | ||||
|     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) | ||||
|         if proxy: | ||||
|             if not data: | ||||
|                 self.xmpp.event('socks_closed', sid) | ||||
|             else: | ||||
|                 self.xmpp.event('socks_recv', {'sid': sid, 'data': 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): | ||||
|         """ Initializes the proxy thread. | ||||
|  | ||||
|         sid        : The StreamID. <str> | ||||
|         requester  : The JID of the requester. <str> | ||||
|         target     : The JID of the target. <str> | ||||
|         proxy_host : The hostname or the IP of the proxy. <str> | ||||
|         proxy_port : The port of the proxy. <str> or <int> | ||||
|         on_recv    : A callback called when data are received from the | ||||
|                      socket. <Callable> | ||||
|         """ | ||||
|  | ||||
|         # 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('Socket connected.') | ||||
|         self.connected.set() | ||||
|  | ||||
|         # Blocks until the socket need to be closed. | ||||
|         self.listen() | ||||
|  | ||||
|         # Closes the socket. | ||||
|         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. | ||||
|         """ | ||||
|  | ||||
|         socket_open = True | ||||
|         while socket_open: | ||||
|             ins = [] | ||||
|             try: | ||||
|                 # Wait any read available data on socket. Timeout | ||||
|                 # after 5 secs. | ||||
|                 ins, out, err = select([self.s, ], [], [], 5) | ||||
|             except Exception as 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) | ||||
|                 if not data: | ||||
|                     socket_open = False | ||||
|  | ||||
|                 self.on_recv(self.sid, data) | ||||
|  | ||||
|     def recv_size(self, the_socket): | ||||
|         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 sock_data: | ||||
|                 return ''.join(total_data) | ||||
|  | ||||
|             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) | ||||
							
								
								
									
										41
									
								
								sleekxmpp/plugins/xep_0065/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								sleekxmpp/plugins/xep_0065/stanza.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| 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' | ||||
|  | ||||
|  | ||||
| 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 = namespace | ||||
|     name = 'streamhost-used' | ||||
|     plugin_attrib = 'streamhost-used' | ||||
|     interfaces = set(('jid',)) | ||||
|  | ||||
|  | ||||
| class Socks5(ElementBase): | ||||
|     """ The query xml element. | ||||
|     """ | ||||
|  | ||||
|     namespace = namespace | ||||
|     name = 'query' | ||||
|     plugin_attrib = 'socks' | ||||
|     interfaces = set(('sid', 'activate')) | ||||
|     sub_interfaces = set(('activate',)) | ||||
|  | ||||
| register_stanza_plugin(Iq, Socks5) | ||||
| register_stanza_plugin(Socks5, StreamHost) | ||||
| register_stanza_plugin(Socks5, StreamHostUsed) | ||||
| @@ -154,7 +154,7 @@ class Iq(RootStanza): | ||||
|         StanzaBase.reply(self, clear) | ||||
|         return self | ||||
|  | ||||
|     def send(self, block=True, timeout=None, callback=None, now=False): | ||||
|     def send(self, block=True, timeout=None, callback=None, now=False, timeout_callback=None): | ||||
|         """ | ||||
|         Send an <iq> stanza over the XML stream. | ||||
|  | ||||
| @@ -181,15 +181,32 @@ class Iq(RootStanza): | ||||
|             now      -- Indicates if the send queue should be skipped and send | ||||
|                         the stanza immediately. Used during stream | ||||
|                         initialization. Defaults to False. | ||||
|             timeout_callback -- Optional reference to a stream handler function. | ||||
|                         Will be executed when the timeout expires before a  | ||||
|                         response has been received with the originally-sent IQ  | ||||
|                         stanza.  Only called if there is a callback parameter | ||||
|                         (and therefore are in async mode). | ||||
|         """ | ||||
|         if timeout is None: | ||||
|             timeout = self.stream.response_timeout | ||||
|         if callback is not None and self['type'] in ('get', 'set'): | ||||
|             handler_name = 'IqCallback_%s' % self['id'] | ||||
|             handler = Callback(handler_name, | ||||
|                                MatcherId(self['id']), | ||||
|                                callback, | ||||
|                                once=True) | ||||
|             if timeout_callback: | ||||
|                 self.callback = callback | ||||
|                 self.timeout_callback = timeout_callback | ||||
|                 self.stream.schedule('IqTimeout_%s' % self['id'],  | ||||
|                                      timeout,  | ||||
|                                      self._fire_timeout,  | ||||
|                                      repeat=False)             | ||||
|                 handler = Callback(handler_name, | ||||
|                                    MatcherId(self['id']), | ||||
|                                    self._handle_result, | ||||
|                                    once=True) | ||||
|             else: | ||||
|                 handler = Callback(handler_name, | ||||
|                                    MatcherId(self['id']), | ||||
|                                    callback, | ||||
|                                    once=True) | ||||
|             self.stream.register_handler(handler) | ||||
|             StanzaBase.send(self, now=now) | ||||
|             return handler_name | ||||
| @@ -206,6 +223,16 @@ class Iq(RootStanza): | ||||
|         else: | ||||
|             return StanzaBase.send(self, now=now) | ||||
|  | ||||
|     def _handle_result(self, iq): | ||||
|         # we got the IQ, so don't fire the timeout | ||||
|         self.stream.scheduler.remove('IqTimeout_%s' % self['id']) | ||||
|         self.callback(iq) | ||||
|  | ||||
|     def _fire_timeout(self): | ||||
|         # don't fire the handler for the IQ, if it finally does come in | ||||
|         self.stream.remove_handler('IqCallback_%s' % self['id']) | ||||
|         self.timeout_callback(self) | ||||
|  | ||||
|     def _set_stanza_values(self, values): | ||||
|         """ | ||||
|         Set multiple stanza interface values using a dictionary. | ||||
|   | ||||
							
								
								
									
										1
									
								
								sleekxmpp/thirdparty/__init__.py
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								sleekxmpp/thirdparty/__init__.py
									
									
									
									
										vendored
									
									
								
							| @@ -8,4 +8,5 @@ try: | ||||
| except: | ||||
|     from sleekxmpp.thirdparty.gnupg import GPG | ||||
|  | ||||
| from sleekxmpp.thirdparty import socks | ||||
| from sleekxmpp.thirdparty.mini_dateutil import tzutc, tzoffset, parse_iso | ||||
|   | ||||
							
								
								
									
										382
									
								
								sleekxmpp/thirdparty/socks.py
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										382
									
								
								sleekxmpp/thirdparty/socks.py
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,382 @@ | ||||
| """SocksiPy - Python SOCKS module. | ||||
| Version 1.00 | ||||
|  | ||||
| Copyright 2006 Dan-Haim. All rights reserved. | ||||
|  | ||||
| Redistribution and use in source and binary forms, with or without modification, | ||||
| are permitted provided that the following conditions are met: | ||||
| 1. Redistributions of source code must retain the above copyright notice, this | ||||
|    list of conditions and the following disclaimer. | ||||
| 2. Redistributions in binary form must reproduce the above copyright notice, | ||||
|    this list of conditions and the following disclaimer in the documentation | ||||
|    and/or other materials provided with the distribution. | ||||
| 3. Neither the name of Dan Haim nor the names of his contributors may be used | ||||
|    to endorse or promote products derived from this software without specific | ||||
|    prior written permission. | ||||
|     | ||||
| THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED | ||||
| WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||||
| MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||||
| EVENT SHALL DAN HAIM OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||
| INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA | ||||
| OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||||
| LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | ||||
| OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE. | ||||
|  | ||||
|  | ||||
| This module provides a standard socket-like interface for Python | ||||
| for tunneling connections through SOCKS proxies. | ||||
|  | ||||
| """ | ||||
|  | ||||
| """ | ||||
|  | ||||
| Minor modifications made by Christopher Gilbert (http://motomastyle.com/) | ||||
| for use in PyLoris (http://pyloris.sourceforge.net/) | ||||
|  | ||||
| Minor modifications made by Mario Vilas (http://breakingcode.wordpress.com/) | ||||
| mainly to merge bug fixes found in Sourceforge | ||||
|  | ||||
| """ | ||||
|  | ||||
| import socket | ||||
| import struct | ||||
| import sys | ||||
|  | ||||
| PROXY_TYPE_SOCKS4 = 1 | ||||
| PROXY_TYPE_SOCKS5 = 2 | ||||
| PROXY_TYPE_HTTP = 3 | ||||
|  | ||||
| _defaultproxy = None | ||||
| _orgsocket = socket.socket | ||||
|  | ||||
| class ProxyError(Exception): pass | ||||
| class GeneralProxyError(ProxyError): pass | ||||
| class Socks5AuthError(ProxyError): pass | ||||
| class Socks5Error(ProxyError): pass | ||||
| class Socks4Error(ProxyError): pass | ||||
| class HTTPError(ProxyError): pass | ||||
|  | ||||
| _generalerrors = ("success", | ||||
|     "invalid data", | ||||
|     "not connected", | ||||
|     "not available", | ||||
|     "bad proxy type", | ||||
|     "bad input") | ||||
|  | ||||
| _socks5errors = ("succeeded", | ||||
|     "general SOCKS server failure", | ||||
|     "connection not allowed by ruleset", | ||||
|     "Network unreachable", | ||||
|     "Host unreachable", | ||||
|     "Connection refused", | ||||
|     "TTL expired", | ||||
|     "Command not supported", | ||||
|     "Address type not supported", | ||||
|     "Unknown error") | ||||
|  | ||||
| _socks5autherrors = ("succeeded", | ||||
|     "authentication is required", | ||||
|     "all offered authentication methods were rejected", | ||||
|     "unknown username or invalid password", | ||||
|     "unknown error") | ||||
|  | ||||
| _socks4errors = ("request granted", | ||||
|     "request rejected or failed", | ||||
|     "request rejected because SOCKS server cannot connect to identd on the client", | ||||
|     "request rejected because the client program and identd report different user-ids", | ||||
|     "unknown error") | ||||
|  | ||||
| def setdefaultproxy(proxytype=None, addr=None, port=None, rdns=True, username=None, password=None): | ||||
|     """setdefaultproxy(proxytype, addr[, port[, rdns[, username[, password]]]]) | ||||
|     Sets a default proxy which all further socksocket objects will use, | ||||
|     unless explicitly changed. | ||||
|     """ | ||||
|     global _defaultproxy | ||||
|     _defaultproxy = (proxytype, addr, port, rdns, username, password) | ||||
|  | ||||
| def wrapmodule(module): | ||||
|     """wrapmodule(module) | ||||
|     Attempts to replace a module's socket library with a SOCKS socket. Must set | ||||
|     a default proxy using setdefaultproxy(...) first. | ||||
|     This will only work on modules that import socket directly into the namespace; | ||||
|     most of the Python Standard Library falls into this category. | ||||
|     """ | ||||
|     if _defaultproxy != None: | ||||
|         module.socket.socket = socksocket | ||||
|     else: | ||||
|         raise GeneralProxyError((4, "no proxy specified")) | ||||
|  | ||||
| class socksocket(socket.socket): | ||||
|     """socksocket([family[, type[, proto]]]) -> socket object | ||||
|     Open a SOCKS enabled socket. The parameters are the same as | ||||
|     those of the standard socket init. In order for SOCKS to work, | ||||
|     you must specify family=AF_INET, type=SOCK_STREAM and proto=0. | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, _sock=None): | ||||
|         _orgsocket.__init__(self, family, type, proto, _sock) | ||||
|         if _defaultproxy != None: | ||||
|             self.__proxy = _defaultproxy | ||||
|         else: | ||||
|             self.__proxy = (None, None, None, None, None, None) | ||||
|         self.__proxysockname = None | ||||
|         self.__proxypeername = None | ||||
|  | ||||
|     def __recvall(self, count): | ||||
|         """__recvall(count) -> data | ||||
|         Receive EXACTLY the number of bytes requested from the socket. | ||||
|         Blocks until the required number of bytes have been received. | ||||
|         """ | ||||
|         data = self.recv(count) | ||||
|         while len(data) < count: | ||||
|             d = self.recv(count-len(data)) | ||||
|             if not d: raise GeneralProxyError((0, "connection closed unexpectedly")) | ||||
|             data = data + d | ||||
|         return data | ||||
|  | ||||
|     def setproxy(self, proxytype=None, addr=None, port=None, rdns=True, username=None, password=None): | ||||
|         """setproxy(proxytype, addr[, port[, rdns[, username[, password]]]]) | ||||
|         Sets the proxy to be used. | ||||
|         proxytype -    The type of the proxy to be used. Three types | ||||
|                 are supported: PROXY_TYPE_SOCKS4 (including socks4a), | ||||
|                 PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP | ||||
|         addr -        The address of the server (IP or DNS). | ||||
|         port -        The port of the server. Defaults to 1080 for SOCKS | ||||
|                 servers and 8080 for HTTP proxy servers. | ||||
|         rdns -        Should DNS queries be preformed on the remote side | ||||
|                 (rather than the local side). The default is True. | ||||
|                 Note: This has no effect with SOCKS4 servers. | ||||
|         username -    Username to authenticate with to the server. | ||||
|                 The default is no authentication. | ||||
|         password -    Password to authenticate with to the server. | ||||
|                 Only relevant when username is also provided. | ||||
|         """ | ||||
|         self.__proxy = (proxytype, addr, port, rdns, username, password) | ||||
|  | ||||
|     def __negotiatesocks5(self, destaddr, destport): | ||||
|         """__negotiatesocks5(self,destaddr,destport) | ||||
|         Negotiates a connection through a SOCKS5 server. | ||||
|         """ | ||||
|         # First we'll send the authentication packages we support. | ||||
|         if (self.__proxy[4]!=None) and (self.__proxy[5]!=None): | ||||
|             # The username/password details were supplied to the | ||||
|             # setproxy method so we support the USERNAME/PASSWORD | ||||
|             # authentication (in addition to the standard none). | ||||
|             self.sendall(struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02)) | ||||
|         else: | ||||
|             # No username/password were entered, therefore we | ||||
|             # only support connections with no authentication. | ||||
|             self.sendall(struct.pack('BBB', 0x05, 0x01, 0x00)) | ||||
|         # We'll receive the server's response to determine which | ||||
|         # method was selected | ||||
|         chosenauth = self.__recvall(2) | ||||
|         if chosenauth[0:1] != chr(0x05).encode(): | ||||
|             self.close() | ||||
|             raise GeneralProxyError((1, _generalerrors[1])) | ||||
|         # Check the chosen authentication method | ||||
|         if chosenauth[1:2] == chr(0x00).encode(): | ||||
|             # No authentication is required | ||||
|             pass | ||||
|         elif chosenauth[1:2] == chr(0x02).encode(): | ||||
|             # Okay, we need to perform a basic username/password | ||||
|             # authentication. | ||||
|             self.sendall(chr(0x01).encode() + chr(len(self.__proxy[4])) + self.__proxy[4] + chr(len(self.__proxy[5])) + self.__proxy[5]) | ||||
|             authstat = self.__recvall(2) | ||||
|             if authstat[0:1] != chr(0x01).encode(): | ||||
|                 # Bad response | ||||
|                 self.close() | ||||
|                 raise GeneralProxyError((1, _generalerrors[1])) | ||||
|             if authstat[1:2] != chr(0x00).encode(): | ||||
|                 # Authentication failed | ||||
|                 self.close() | ||||
|                 raise Socks5AuthError((3, _socks5autherrors[3])) | ||||
|             # Authentication succeeded | ||||
|         else: | ||||
|             # Reaching here is always bad | ||||
|             self.close() | ||||
|             if chosenauth[1] == chr(0xFF).encode(): | ||||
|                 raise Socks5AuthError((2, _socks5autherrors[2])) | ||||
|             else: | ||||
|                 raise GeneralProxyError((1, _generalerrors[1])) | ||||
|         # Now we can request the actual connection | ||||
|         req = struct.pack('BBB', 0x05, 0x01, 0x00) | ||||
|         # If the given destination address is an IP address, we'll | ||||
|         # use the IPv4 address request even if remote resolving was specified. | ||||
|         try: | ||||
|             ipaddr = socket.inet_aton(destaddr) | ||||
|             req = req + chr(0x01).encode() + ipaddr | ||||
|         except socket.error: | ||||
|             # Well it's not an IP number,  so it's probably a DNS name. | ||||
|             if self.__proxy[3]: | ||||
|                 # Resolve remotely | ||||
|                 ipaddr = None | ||||
|                 req = req + chr(0x03).encode() + chr(len(destaddr)).encode() + destaddr | ||||
|             else: | ||||
|                 # Resolve locally | ||||
|                 ipaddr = socket.inet_aton(socket.gethostbyname(destaddr)) | ||||
|                 req = req + chr(0x01).encode() + ipaddr | ||||
|         req = req + struct.pack(">H", destport) | ||||
|         self.sendall(req) | ||||
|         # Get the response | ||||
|         resp = self.__recvall(4) | ||||
|         if resp[0:1] != chr(0x05).encode(): | ||||
|             self.close() | ||||
|             raise GeneralProxyError((1, _generalerrors[1])) | ||||
|         elif resp[1:2] != chr(0x00).encode(): | ||||
|             # Connection failed | ||||
|             self.close() | ||||
|             if ord(resp[1:2])<=8: | ||||
|                 raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])])) | ||||
|             else: | ||||
|                 raise Socks5Error((9, _socks5errors[9])) | ||||
|         # Get the bound address/port | ||||
|         elif resp[3:4] == chr(0x01).encode(): | ||||
|             boundaddr = self.__recvall(4) | ||||
|         elif resp[3:4] == chr(0x03).encode(): | ||||
|             resp = resp + self.recv(1) | ||||
|             boundaddr = self.__recvall(ord(resp[4:5])) | ||||
|         else: | ||||
|             self.close() | ||||
|             raise GeneralProxyError((1,_generalerrors[1])) | ||||
|         boundport = struct.unpack(">H", self.__recvall(2))[0] | ||||
|         self.__proxysockname = (boundaddr, boundport) | ||||
|         if ipaddr != None: | ||||
|             self.__proxypeername = (socket.inet_ntoa(ipaddr), destport) | ||||
|         else: | ||||
|             self.__proxypeername = (destaddr, destport) | ||||
|  | ||||
|     def getproxysockname(self): | ||||
|         """getsockname() -> address info | ||||
|         Returns the bound IP address and port number at the proxy. | ||||
|         """ | ||||
|         return self.__proxysockname | ||||
|  | ||||
|     def getproxypeername(self): | ||||
|         """getproxypeername() -> address info | ||||
|         Returns the IP and port number of the proxy. | ||||
|         """ | ||||
|         return _orgsocket.getpeername(self) | ||||
|  | ||||
|     def getpeername(self): | ||||
|         """getpeername() -> address info | ||||
|         Returns the IP address and port number of the destination | ||||
|         machine (note: getproxypeername returns the proxy) | ||||
|         """ | ||||
|         return self.__proxypeername | ||||
|  | ||||
|     def __negotiatesocks4(self,destaddr,destport): | ||||
|         """__negotiatesocks4(self,destaddr,destport) | ||||
|         Negotiates a connection through a SOCKS4 server. | ||||
|         """ | ||||
|         # Check if the destination address provided is an IP address | ||||
|         rmtrslv = False | ||||
|         try: | ||||
|             ipaddr = socket.inet_aton(destaddr) | ||||
|         except socket.error: | ||||
|             # It's a DNS name. Check where it should be resolved. | ||||
|             if self.__proxy[3]: | ||||
|                 ipaddr = struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01) | ||||
|                 rmtrslv = True | ||||
|             else: | ||||
|                 ipaddr = socket.inet_aton(socket.gethostbyname(destaddr)) | ||||
|         # Construct the request packet | ||||
|         req = struct.pack(">BBH", 0x04, 0x01, destport) + ipaddr | ||||
|         # The username parameter is considered userid for SOCKS4 | ||||
|         if self.__proxy[4] != None: | ||||
|             req = req + self.__proxy[4] | ||||
|         req = req + chr(0x00).encode() | ||||
|         # DNS name if remote resolving is required | ||||
|         # NOTE: This is actually an extension to the SOCKS4 protocol | ||||
|         # called SOCKS4A and may not be supported in all cases. | ||||
|         if rmtrslv: | ||||
|             req = req + destaddr + chr(0x00).encode() | ||||
|         self.sendall(req) | ||||
|         # Get the response from the server | ||||
|         resp = self.__recvall(8) | ||||
|         if resp[0:1] != chr(0x00).encode(): | ||||
|             # Bad data | ||||
|             self.close() | ||||
|             raise GeneralProxyError((1,_generalerrors[1])) | ||||
|         if resp[1:2] != chr(0x5A).encode(): | ||||
|             # Server returned an error | ||||
|             self.close() | ||||
|             if ord(resp[1:2]) in (91, 92, 93): | ||||
|                 self.close() | ||||
|                 raise Socks4Error((ord(resp[1:2]), _socks4errors[ord(resp[1:2]) - 90])) | ||||
|             else: | ||||
|                 raise Socks4Error((94, _socks4errors[4])) | ||||
|         # Get the bound address/port | ||||
|         self.__proxysockname = (socket.inet_ntoa(resp[4:]), struct.unpack(">H", resp[2:4])[0]) | ||||
|         if rmtrslv != None: | ||||
|             self.__proxypeername = (socket.inet_ntoa(ipaddr), destport) | ||||
|         else: | ||||
|             self.__proxypeername = (destaddr, destport) | ||||
|  | ||||
|     def __negotiatehttp(self, destaddr, destport): | ||||
|         """__negotiatehttp(self,destaddr,destport) | ||||
|         Negotiates a connection through an HTTP server. | ||||
|         """ | ||||
|         # If we need to resolve locally, we do this now | ||||
|         if not self.__proxy[3]: | ||||
|             addr = socket.gethostbyname(destaddr) | ||||
|         else: | ||||
|             addr = destaddr | ||||
|         self.sendall(("CONNECT " + addr + ":" + str(destport) + " HTTP/1.1\r\n" + "Host: " + destaddr + "\r\n\r\n").encode()) | ||||
|         # We read the response until we get the string "\r\n\r\n" | ||||
|         resp = self.recv(1) | ||||
|         while resp.find("\r\n\r\n".encode()) == -1: | ||||
|             resp = resp + self.recv(1) | ||||
|         # We just need the first line to check if the connection | ||||
|         # was successful | ||||
|         statusline = resp.splitlines()[0].split(" ".encode(), 2) | ||||
|         if statusline[0] not in ("HTTP/1.0".encode(), "HTTP/1.1".encode()): | ||||
|             self.close() | ||||
|             raise GeneralProxyError((1, _generalerrors[1])) | ||||
|         try: | ||||
|             statuscode = int(statusline[1]) | ||||
|         except ValueError: | ||||
|             self.close() | ||||
|             raise GeneralProxyError((1, _generalerrors[1])) | ||||
|         if statuscode != 200: | ||||
|             self.close() | ||||
|             raise HTTPError((statuscode, statusline[2])) | ||||
|         self.__proxysockname = ("0.0.0.0", 0) | ||||
|         self.__proxypeername = (addr, destport) | ||||
|  | ||||
|     def connect(self, destpair): | ||||
|         """connect(self, despair) | ||||
|         Connects to the specified destination through a proxy. | ||||
|         destpar - A tuple of the IP/DNS address and the port number. | ||||
|         (identical to socket's connect). | ||||
|         To select the proxy server use setproxy(). | ||||
|         """ | ||||
|         # Do a minimal input check first | ||||
|         if (not type(destpair) in (list,tuple)) or (len(destpair) < 2) or (type(destpair[0]) != type('')) or (type(destpair[1]) != int): | ||||
|             raise GeneralProxyError((5, _generalerrors[5])) | ||||
|         if self.__proxy[0] == PROXY_TYPE_SOCKS5: | ||||
|             if self.__proxy[2] != None: | ||||
|                 portnum = self.__proxy[2] | ||||
|             else: | ||||
|                 portnum = 1080 | ||||
|             _orgsocket.connect(self, (self.__proxy[1], portnum)) | ||||
|             self.__negotiatesocks5(destpair[0], destpair[1]) | ||||
|         elif self.__proxy[0] == PROXY_TYPE_SOCKS4: | ||||
|             if self.__proxy[2] != None: | ||||
|                 portnum = self.__proxy[2] | ||||
|             else: | ||||
|                 portnum = 1080 | ||||
|             _orgsocket.connect(self,(self.__proxy[1], portnum)) | ||||
|             self.__negotiatesocks4(destpair[0], destpair[1]) | ||||
|         elif self.__proxy[0] == PROXY_TYPE_HTTP: | ||||
|             if self.__proxy[2] != None: | ||||
|                 portnum = self.__proxy[2] | ||||
|             else: | ||||
|                 portnum = 8080 | ||||
|             _orgsocket.connect(self,(self.__proxy[1], portnum)) | ||||
|             self.__negotiatehttp(destpair[0], destpair[1]) | ||||
|         elif self.__proxy[0] == None: | ||||
|             _orgsocket.connect(self, (destpair[0], destpair[1])) | ||||
|         else: | ||||
|             raise GeneralProxyError((4, _generalerrors[4])) | ||||
| @@ -153,6 +153,35 @@ class TestHandlers(SleekTest): | ||||
|         self.failUnless(events == ['foo'], | ||||
|                 "Iq callback was not executed: %s" % events) | ||||
|  | ||||
|     def testIqTimeoutCallback(self): | ||||
|         """Test that iq.send(tcallback=handle_foo, timeout_callback=handle_timeout) works.""" | ||||
|         events = [] | ||||
|  | ||||
|         def handle_foo(iq): | ||||
|             events.append('foo') | ||||
|  | ||||
|         def handle_timeout(iq): | ||||
|             events.append('timeout') | ||||
|  | ||||
|         iq = self.Iq() | ||||
|         iq['type'] = 'get' | ||||
|         iq['id'] = 'test-foo' | ||||
|         iq['to'] = 'user@localhost' | ||||
|         iq['query'] = 'foo' | ||||
|         iq.send(callback=handle_foo, timeout_callback=handle_timeout, timeout=0.05) | ||||
|  | ||||
|         self.send(""" | ||||
|           <iq type="get" id="test-foo" to="user@localhost"> | ||||
|             <query xmlns="foo" /> | ||||
|           </iq> | ||||
|         """) | ||||
|  | ||||
|         # Give event queue time to process | ||||
|         time.sleep(0.1) | ||||
|  | ||||
|         self.failUnless(events == ['timeout'], | ||||
|                 "Iq timeout was not executed: %s" % events) | ||||
|  | ||||
|     def testMultipleHandlersForStanza(self): | ||||
|         """ | ||||
|         Test that multiple handlers for a single stanza work | ||||
|   | ||||
		Reference in New Issue
	
	Block a user