1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 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') 
 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 
 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   
 73          raise NotImplementedError 
  74   
 76          self.onreceive(self.old_on_receive) 
 77          self.on_success() 
   78   
 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   
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               
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() 
 121      """ 
122      SOCKS5 proxy connection class. Allows to use SOCKS5 proxies with 
123      (optionally) simple authentication (only USERNAME/PASSWORD auth) 
124      """ 
125   
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   
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   
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           
180          req = "\x05\x01\x00" 
181           
182           
183          try: 
184              self.ipaddr = socket.inet_aton(self.xmpp_server[0]) 
185              req = req + "\x01" + self.ipaddr 
186          except socket.error: 
187               
188   
189               
190              self.ipaddr = None 
191              req = req + "\x03" + chr(len(self.xmpp_server[0])) + self.xmpp_server[0] 
192   
193   
194   
195   
196          req = req + struct.pack(">H", self.xmpp_server[1]) 
197          self.onreceive(self._on_req_sent) 
198          self.send(req) 
 199   
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               
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           
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