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()
118
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