Mercurial > libervia-backend
comparison src/plugins/plugin_xep_0065.py @ 1570:37d4be4a9fed
plugins XEP-0260, XEP-0065: proxy handling:
- XEP-0065: Candidate.activate launch proxy activation
- XEP-0065: a candidate is individually connected with connectCandidate
- transport-info action handling can now manage candidate and proxy infos
author | Goffi <goffi@goffi.org> |
---|---|
date | Sun, 08 Nov 2015 14:44:33 +0100 |
parents | 44854fb5d3b2 |
children | d5f59ba166fe |
comparison
equal
deleted
inserted
replaced
1569:44854fb5d3b2 | 1570:37d4be4a9fed |
---|---|
255 multiplier = 10 | 255 multiplier = 10 |
256 else: | 256 else: |
257 raise exceptions.InternalError(u"Unknown {} type !".format(self.type)) | 257 raise exceptions.InternalError(u"Unknown {} type !".format(self.type)) |
258 return 2**16 * multiplier + self._local_priority | 258 return 2**16 * multiplier + self._local_priority |
259 | 259 |
260 def activate(self, sid, peer_jid, client): | |
261 """Activate the proxy candidate | |
262 | |
263 Send activation request as explained in XEP-0065 § 6.3.5 | |
264 Must only be used with proxy candidates | |
265 @param sid(unicode): session id (same as for getSessionHash) | |
266 @param peer_jid(jid.JID): jid of the other peer | |
267 @return (D(domish.Element)): IQ result (or error) | |
268 """ | |
269 assert self.type == XEP_0065.TYPE_PROXY | |
270 iq_elt = client.IQ() | |
271 iq_elt['to'] = self.jid.full() | |
272 query_elt = iq_elt.addElement((NS_BS, 'query')) | |
273 query_elt['sid'] = sid | |
274 query_elt.addElement('activate', content=peer_jid.full()) | |
275 return iq_elt.send() | |
276 | |
260 def startTransfer(self, session_hash=None): | 277 def startTransfer(self, session_hash=None): |
261 self.factory.startTransfer(session_hash) | 278 self.factory.startTransfer(session_hash) |
262 | 279 |
263 | 280 |
264 def getSessionHash(from_jid, to_jid, sid): | 281 def getSessionHash(from_jid, to_jid, sid): |
278 def __init__(self, session_hash=None): | 295 def __init__(self, session_hash=None): |
279 """ | 296 """ |
280 @param session_hash(str): hash of the session | 297 @param session_hash(str): hash of the session |
281 must only be used in client mode | 298 must only be used in client mode |
282 """ | 299 """ |
283 log.debug(_("Protocol init")) | |
284 self.connection = defer.Deferred() # called when connection/auth is done | 300 self.connection = defer.Deferred() # called when connection/auth is done |
285 if session_hash is not None: | 301 if session_hash is not None: |
286 self.server_mode = False | 302 self.server_mode = False |
287 self._session_hash = session_hash | 303 self._session_hash = session_hash |
288 self.state = STATE_CLIENT_INITIAL | 304 self.state = STATE_CLIENT_INITIAL |
311 log.debug("starting negotiation (client mode)") | 327 log.debug("starting negotiation (client mode)") |
312 self.state = STATE_CLIENT_AUTH | 328 self.state = STATE_CLIENT_AUTH |
313 self.transport.write(struct.pack('!3B', SOCKS5_VER, 1, AUTHMECH_ANON)) | 329 self.transport.write(struct.pack('!3B', SOCKS5_VER, 1, AUTHMECH_ANON)) |
314 | 330 |
315 def _parseNegotiation(self): | 331 def _parseNegotiation(self): |
316 log.debug("_parseNegotiation") | |
317 try: | 332 try: |
318 # Parse out data | 333 # Parse out data |
319 ver, nmethod = struct.unpack('!BB', self.buf[:2]) | 334 ver, nmethod = struct.unpack('!BB', self.buf[:2]) |
320 methods = struct.unpack('%dB' % nmethod, self.buf[2:nmethod + 2]) | 335 methods = struct.unpack('%dB' % nmethod, self.buf[2:nmethod + 2]) |
321 | 336 |
346 self.transport.loseConnection() | 361 self.transport.loseConnection() |
347 except struct.error: | 362 except struct.error: |
348 pass | 363 pass |
349 | 364 |
350 def _parseUserPass(self): | 365 def _parseUserPass(self): |
351 log.debug("_parseUserPass") | |
352 try: | 366 try: |
353 # Parse out data | 367 # Parse out data |
354 ver, ulen = struct.unpack('BB', self.buf[:2]) | 368 ver, ulen = struct.unpack('BB', self.buf[:2]) |
355 uname, = struct.unpack('%ds' % ulen, self.buf[2:ulen + 2]) | 369 uname, = struct.unpack('%ds' % ulen, self.buf[2:ulen + 2]) |
356 plen, = struct.unpack('B', self.buf[ulen + 2]) | 370 plen, = struct.unpack('B', self.buf[ulen + 2]) |
368 self.transport.loseConnection() | 382 self.transport.loseConnection() |
369 except struct.error: | 383 except struct.error: |
370 pass | 384 pass |
371 | 385 |
372 def sendErrorReply(self, errorcode): | 386 def sendErrorReply(self, errorcode): |
373 log.debug("sendErrorReply") | |
374 # Any other address types are not supported | 387 # Any other address types are not supported |
375 result = struct.pack('!BBBBIH', SOCKS5_VER, errorcode, 0, 1, 0, 0) | 388 result = struct.pack('!BBBBIH', SOCKS5_VER, errorcode, 0, 1, 0, 0) |
376 self.transport.write(result) | 389 self.transport.write(result) |
377 self.transport.loseConnection() | 390 self.transport.loseConnection() |
378 | 391 |
379 def _parseRequest(self): | 392 def _parseRequest(self): |
380 log.debug("_parseRequest") | |
381 try: | 393 try: |
382 # Parse out data and trim buffer accordingly | 394 # Parse out data and trim buffer accordingly |
383 ver, cmd, rsvd, self.addressType = struct.unpack('!BBBB', self.buf[:4]) | 395 ver, cmd, rsvd, self.addressType = struct.unpack('!BBBB', self.buf[:4]) |
384 | 396 |
385 # Ensure we actually support the requested address type | 397 # Ensure we actually support the requested address type |
418 except struct.error: | 430 except struct.error: |
419 # The buffer is probably not complete, we need to wait more | 431 # The buffer is probably not complete, we need to wait more |
420 return None | 432 return None |
421 | 433 |
422 def _makeRequest(self): | 434 def _makeRequest(self): |
423 log.debug("_makeRequest") | |
424 # sha1 = getSessionHash(self.data["from"], self.data["to"], self.sid) | 435 # sha1 = getSessionHash(self.data["from"], self.data["to"], self.sid) |
425 hash_ = self._session_hash | 436 hash_ = self._session_hash |
426 request = struct.pack('!5B%dsH' % len(hash_), SOCKS5_VER, CMD_CONNECT, 0, ADDR_DOMAINNAME, len(hash_), hash_, 0) | 437 request = struct.pack('!5B%dsH' % len(hash_), SOCKS5_VER, CMD_CONNECT, 0, ADDR_DOMAINNAME, len(hash_), hash_, 0) |
427 self.transport.write(request) | 438 self.transport.write(request) |
428 self.state = STATE_CLIENT_REQUEST | 439 self.state = STATE_CLIENT_REQUEST |
429 | 440 |
430 def _parseRequestReply(self): | 441 def _parseRequestReply(self): |
431 log.debug("_parseRequestReply") | |
432 try: | 442 try: |
433 ver, rep, rsvd, self.addressType = struct.unpack('!BBBB', self.buf[:4]) | 443 ver, rep, rsvd, self.addressType = struct.unpack('!BBBB', self.buf[:4]) |
434 # Ensure we actually support the requested address type | 444 # Ensure we actually support the requested address type |
435 if self.addressType not in self.supportedAddrs: | 445 if self.addressType not in self.supportedAddrs: |
436 self.sendErrorReply(REPLY_ADDR_NOT_SUPPORTED) | 446 self.sendErrorReply(REPLY_ADDR_NOT_SUPPORTED) |
470 log.debug(u"Socks5 connectionMade (mode = {})".format("server" if self.state == STATE_INITIAL else "client")) | 480 log.debug(u"Socks5 connectionMade (mode = {})".format("server" if self.state == STATE_INITIAL else "client")) |
471 if self.state == STATE_CLIENT_INITIAL: | 481 if self.state == STATE_CLIENT_INITIAL: |
472 self._startNegotiation() | 482 self._startNegotiation() |
473 | 483 |
474 def connectRequested(self, addr, port): | 484 def connectRequested(self, addr, port): |
475 log.debug("connectRequested") | |
476 | |
477 # Check that this session is expected | 485 # Check that this session is expected |
478 if not self.factory.addToSession(addr, self): | 486 if not self.factory.addToSession(addr, self): |
479 self.sendErrorReply(REPLY_CONN_REFUSED) | 487 self.sendErrorReply(REPLY_CONN_REFUSED) |
480 log.warning(u"Unexpected connection request received from {host}" | 488 log.warning(u"Unexpected connection request received from {host}" |
481 .format(host=self.transport.getPeer().host)) | 489 .format(host=self.transport.getPeer().host)) |
495 def fileTransfered(self, d): | 503 def fileTransfered(self, d): |
496 log.info(_("File transfer completed, closing connection")) | 504 log.info(_("File transfer completed, closing connection")) |
497 self.transport.loseConnection() | 505 self.transport.loseConnection() |
498 | 506 |
499 def connectCompleted(self, remotehost, remoteport): | 507 def connectCompleted(self, remotehost, remoteport): |
500 log.debug("connectCompleted") | |
501 if self.addressType == ADDR_IPV4: | 508 if self.addressType == ADDR_IPV4: |
502 result = struct.pack('!BBBBIH', SOCKS5_VER, REPLY_SUCCESS, 0, 1, remotehost, remoteport) | 509 result = struct.pack('!BBBBIH', SOCKS5_VER, REPLY_SUCCESS, 0, 1, remotehost, remoteport) |
503 elif self.addressType == ADDR_DOMAINNAME: | 510 elif self.addressType == ADDR_DOMAINNAME: |
504 result = struct.pack('!BBBBB%dsH' % len(remotehost), SOCKS5_VER, REPLY_SUCCESS, 0, | 511 result = struct.pack('!BBBBB%dsH' % len(remotehost), SOCKS5_VER, REPLY_SUCCESS, 0, |
505 ADDR_DOMAINNAME, len(remotehost), remotehost, remoteport) | 512 ADDR_DOMAINNAME, len(remotehost), remotehost, remoteport) |
869 @return (D): Deferred fired when factory connection is done or has failed | 876 @return (D): Deferred fired when factory connection is done or has failed |
870 """ | 877 """ |
871 candidate.factory.connector = connector | 878 candidate.factory.connector = connector |
872 return candidate.factory.connection | 879 return candidate.factory.connection |
873 | 880 |
881 def connectCandidate(self, candidate, session_hash, delay=None, profile=C.PROF_KEY_NONE): | |
882 """"Connect to a candidate | |
883 | |
884 Connection will be done with a Socks5ClientFactory | |
885 | |
886 @param candidate(Candidate): candidate to connect to | |
887 @param session_hash(unicode): hash of the session | |
888 hash is the same as hostname computer in XEP-0065 § 5.3.2 #1 | |
889 @param delay(None, float): optional delay to wait before connection, in seconds | |
890 @param profile: %(doc_profile)s | |
891 @return (D): Deferred launched when TCP connection + Socks5 connection is done | |
892 """ | |
893 factory = Socks5ClientFactory(self, session_hash, profile) | |
894 candidate.factory = factory | |
895 if delay is None: | |
896 d = defer.succeed(candidate.host) | |
897 else: | |
898 d = sat_defer.DelayedDeferred(delay, candidate.host) | |
899 d.addCallback(reactor.connectTCP, candidate.port, factory) | |
900 d.addCallback(self._addConnector, candidate) | |
901 return d | |
902 | |
874 def tryCandidates(self, candidates, session_hash, connection_cb=None, connection_eb=None, profile=C.PROF_KEY_NONE): | 903 def tryCandidates(self, candidates, session_hash, connection_cb=None, connection_eb=None, profile=C.PROF_KEY_NONE): |
875 defers_list = [] | 904 defers_list = [] |
876 | 905 |
877 for candidate in candidates: | 906 for candidate in candidates: |
878 factory = Socks5ClientFactory(self, session_hash, profile) | |
879 candidate.factory = factory | |
880 delay = CANDIDATE_DELAY * len(defers_list) | 907 delay = CANDIDATE_DELAY * len(defers_list) |
881 if candidate.type == XEP_0065.TYPE_PROXY: | 908 if candidate.type == XEP_0065.TYPE_PROXY: |
882 delay += CANDIDATE_DELAY_PROXY | 909 delay += CANDIDATE_DELAY_PROXY |
883 d = sat_defer.DelayedDeferred(delay, candidate.host) | 910 d = self.connectCandidate(candidate, session_hash, delay, profile) |
884 d.addCallback(reactor.connectTCP, candidate.port, factory) | |
885 d.addCallback(self._addConnector, candidate) | |
886 if connection_cb is not None: | 911 if connection_cb is not None: |
887 d.addCallback(lambda dummy, candidate=candidate, profile=profile: connection_cb(candidate, profile)) | 912 d.addCallback(lambda dummy, candidate=candidate, profile=profile: connection_cb(candidate, profile)) |
888 if connection_eb is not None: | 913 if connection_eb is not None: |
889 d.addErrback(connection_eb, candidate, profile) | 914 d.addErrback(connection_eb, candidate, profile) |
890 defers_list.append(d) | 915 defers_list.append(d) |