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)