Package nbxmpp :: Module proxy_connectors
[hide private]
[frames] | no frames]

Source Code for Module nbxmpp.proxy_connectors

  1  ##   proxy_connectors.py 
  2  ##       based on transports_nb.py 
  3  ## 
  4  ##   Copyright (C) 2003-2004 Alexey "Snake" Nezhdanov 
  5  ##       modified by Dimitur Kirov <dkirov@gmail.com> 
  6  ##       modified by Tomas Karasek <tom.to.the.k@gmail.com> 
  7  ## 
  8  ##   This program is free software; you can redistribute it and/or modify 
  9  ##   it under the terms of the GNU General Public License as published by 
 10  ##   the Free Software Foundation; either version 2, or (at your option) 
 11  ##   any later version. 
 12  ## 
 13  ##   This program is distributed in the hope that it will be useful, 
 14  ##   but WITHOUT ANY WARRANTY; without even the implied warranty of 
 15  ##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 16  ##   GNU General Public License for more details. 
 17   
 18  """ 
 19  Module containing classes for proxy connecting. So far its HTTP CONNECT and 
 20  SOCKS5 proxy 
 21   
 22  Authentication to NTLM (Microsoft implementation) proxies can be next. 
 23  """ 
 24   
 25  import struct, socket, base64 
 26  import logging 
 27  log = logging.getLogger('nbxmpp.proxy_connectors') 
28 29 -class ProxyConnector:
30 """ 31 Interface for proxy-connecting object - when tunnneling XMPP over proxies, 32 some connecting process usually has to be done before opening stream. Proxy 33 connectors are used right after TCP connection is estabilished 34 """ 35
36 - def __init__(self, send_method, onreceive, old_on_receive, on_success, 37 on_failure, xmpp_server, proxy_creds=(None, None)):
38 """ 39 Creates proxy connector, starts connecting immediately and gives control 40 back to transport afterwards 41 42 :param send_method: transport send method 43 :param onreceive: method to set on_receive callbacks 44 :param old_on_receive: on_receive callback that should be set when 45 proxy connection was successful 46 :param on_success: called after proxy connection was successfully opened 47 :param on_failure: called when errors occured while connecting 48 :param xmpp_server: tuple of (hostname, port) 49 :param proxy_creds: tuple of (proxy_user, proxy_credentials) 50 """ 51 self.send = send_method 52 self.onreceive = onreceive 53 self.old_on_receive = old_on_receive 54 self.on_success = on_success 55 self.on_failure = on_failure 56 self.xmpp_server = xmpp_server 57 self.proxy_user, self.proxy_pass = proxy_creds 58 self.old_on_receive = old_on_receive 59 60 self.start_connecting()
61 62 @classmethod
63 - def get_instance(cls, *args, **kwargs):
64 """ 65 Factory Method for object creation 66 67 Use this instead of directly initializing the class in order to make unit 68 testing much easier. 69 """ 70 return cls(*args, **kwargs)
71
72 - def start_connecting(self):
73 raise NotImplementedError
74
75 - def connecting_over(self):
76 self.onreceive(self.old_on_receive) 77 self.on_success()
78
79 -class HTTPCONNECTConnector(ProxyConnector):
80 - def start_connecting(self):
81 """ 82 Connect to a proxy, supply login and password to it (if were specified 83 while creating instance). Instruct proxy to make connection to the target 84 server. 85 """ 86 log.info('Proxy server contacted, performing authentification') 87 connector = ['CONNECT %s:%s HTTP/1.1' % self.xmpp_server, 88 'Proxy-Connection: Keep-Alive', 89 'Pragma: no-cache', 90 'Host: %s:%s' % self.xmpp_server, 91 'User-Agent: Gajim'] 92 if self.proxy_user and self.proxy_pass: 93 credentials = '%s:%s' % (self.proxy_user, self.proxy_pass) 94 credentials = base64.encodestring(credentials).strip() 95 connector.append('Proxy-Authorization: Basic '+credentials) 96 connector.append('\r\n') 97 self.onreceive(self._on_headers_sent) 98 self.send('\r\n'.join(connector))
99
100 - def _on_headers_sent(self, reply):
101 if reply is None: 102 return 103 self.reply = reply.replace('\r', '') 104 try: 105 proto, code, desc = reply.split('\n')[0].split(' ', 2) 106 except: 107 log.error("_on_headers_sent:", exc_info=True) 108 #traceback.print_exc() 109 self.on_failure('Invalid proxy reply') 110 return 111 if code <> '200': 112 log.error('Invalid proxy reply: %s %s %s' % (proto, code, desc)) 113 self.on_failure('Invalid proxy reply') 114 return 115 if len(reply) != 2: 116 pass 117 self.connecting_over()
118
119 120 -class SOCKS5Connector(ProxyConnector):
121 """ 122 SOCKS5 proxy connection class. Allows to use SOCKS5 proxies with 123 (optionally) simple authentication (only USERNAME/PASSWORD auth) 124 """ 125
126 - def start_connecting(self):
127 log.info('Proxy server contacted, performing authentification') 128 if self.proxy_user and self.proxy_pass: 129 to_send = '\x05\x02\x00\x02' 130 else: 131 to_send = '\x05\x01\x00' 132 self.onreceive(self._on_greeting_sent) 133 self.send(to_send)
134
135 - def _on_greeting_sent(self, reply):
136 if reply is None: 137 return 138 if len(reply) != 2: 139 self.on_failure('Invalid proxy reply') 140 return 141 if reply[0] != '\x05': 142 log.info('Invalid proxy reply') 143 self.on_failure('Invalid proxy reply') 144 return 145 if reply[1] == '\x00': 146 return self._on_proxy_auth('\x01\x00') 147 elif reply[1] == '\x02': 148 to_send = '\x01' + chr(len(self.proxy_user)) + self.proxy_user +\ 149 chr(len(self.proxy_pass)) + self.proxy_pass 150 self.onreceive(self._on_proxy_auth) 151 self.send(to_send) 152 else: 153 if reply[1] == '\xff': 154 log.error('Authentification to proxy impossible: no acceptable ' 155 'auth method') 156 self.on_failure('Authentification to proxy impossible: no ' 157 'acceptable authentification method') 158 return 159 log.error('Invalid proxy reply') 160 self.on_failure('Invalid proxy reply') 161 return
162
163 - def _on_proxy_auth(self, reply):
164 if reply is None: 165 return 166 if len(reply) != 2: 167 log.error('Invalid proxy reply') 168 self.on_failure('Invalid proxy reply') 169 return 170 if reply[0] != '\x01': 171 log.error('Invalid proxy reply') 172 self.on_failure('Invalid proxy reply') 173 return 174 if reply[1] != '\x00': 175 log.error('Authentification to proxy failed') 176 self.on_failure('Authentification to proxy failed') 177 return 178 log.info('Authentification successfull. Jabber server contacted.') 179 # Request connection 180 req = "\x05\x01\x00" 181 # If the given destination address is an IP address, we'll 182 # use the IPv4 address request even if remote resolving was specified. 183 try: 184 self.ipaddr = socket.inet_aton(self.xmpp_server[0]) 185 req = req + "\x01" + self.ipaddr 186 except socket.error: 187 # Well it's not an IP number, so it's probably a DNS name. 188 # if self.__proxy[3]==True: 189 # Resolve remotely 190 self.ipaddr = None 191 req = req + "\x03" + chr(len(self.xmpp_server[0])) + self.xmpp_server[0] 192 # else: 193 # # Resolve locally 194 # self.ipaddr = socket.inet_aton(socket.gethostbyname(self.xmpp_server[0])) 195 # req = req + "\x01" + ipaddr 196 req = req + struct.pack(">H", self.xmpp_server[1]) 197 self.onreceive(self._on_req_sent) 198 self.send(req)
199
200 - def _on_req_sent(self, reply):
201 if reply is None: 202 return 203 if len(reply) < 10: 204 log.error('Invalid proxy reply') 205 self.on_failure('Invalid proxy reply') 206 return 207 if reply[0] != '\x05': 208 log.error('Invalid proxy reply') 209 self.on_failure('Invalid proxy reply') 210 return 211 if reply[1] != "\x00": 212 # Connection failed 213 if ord(reply[1])<9: 214 errors = ['general SOCKS server failure', 215 'connection not allowed by ruleset', 216 'Network unreachable', 217 'Host unreachable', 218 'Connection refused', 219 'TTL expired', 220 'Command not supported', 221 'Address type not supported' 222 ] 223 txt = errors[ord(reply[1])-1] 224 else: 225 txt = 'Invalid proxy reply' 226 log.error(txt) 227 self.on_failure(txt) 228 return 229 # Get the bound address/port 230 elif reply[3] == "\x01": 231 begin, end = 3, 7 232 elif reply[3] == "\x03": 233 begin, end = 4, 4 + reply[4] 234 else: 235 log.error('Invalid proxy reply') 236 self.on_failure('Invalid proxy reply') 237 return 238 self.connecting_over()
239