def _sipreceiver(self, stack, maxsize=16386): '''Handle the messages or connections on the given SIP stack's socket, and pass it to the stack so that stack can invoke appropriate callback on this object such as receivedRequest.''' sock = stack.sock def tcpreceiver( sock, remote): # handle the messages on the given TCP connection. while True: data = yield multitask.recv(sock, maxsize) if _debug: print '%r=>%r on type=%r\n%s' % ( remote, sock.getsockname(), sock.type, data) if data: stack.received(data, remote) while True: if sock.type == socket.SOCK_DGRAM: data, remote = yield multitask.recvfrom(sock, maxsize) if _debug: print '%r=>%r on type=%r\n%s' % ( remote, sock.getsockname(), sock.type, data) if data: stack.received(data, remote) elif sock.type == socket.SOCK_STREAM: conn, remote = yield multitask.accept(sock) if conn: self.conn[remote] = conn multitask.add(tcpreceiver(conn, remote)) else: raise ValueError, 'invalid socket type'
def _sipreceiver(self, stack, maxsize=16386): '''Handle the messages or connections on the given SIP stack's socket, and pass it to the stack so that stack can invoke appropriate callback on this object such as receivedRequest.''' sock = stack.sock def tcpreceiver(sock, remote): # handle the messages on the given TCP connection. while True: data = yield multitask.recv(sock, maxsize) if _debug: print '%r=>%r on type=%r\n%s' % ( remote, sock.getsockname(), sock.type, data) if data: stack.received(data, remote) while True: if sock.type == socket.SOCK_DGRAM: data, remote = yield multitask.recvfrom(sock, maxsize) if _debug: print '%r=>%r on type=%r\n%s' % ( remote, sock.getsockname(), sock.type, data) if data: stack.received(data, remote) elif sock.type == socket.SOCK_STREAM: conn, remote = yield multitask.accept(sock) if conn: self.conn[remote] = conn multitask.add(tcpreceiver(conn, remote)) else: raise ValueError, 'invalid socket type'
def _sipreceiver(self, stack, maxsize=16386): '''Handle the messages or connections on the given SIP stack's socket, and pass it to the stack so that stack can invoke appropriate callback on this object such as receivedRequest.''' sock = stack.sock while True: if sock.type == socket.SOCK_DGRAM: data, remote = yield multitask.recvfrom(sock, maxsize) logger.debug('%r=>%r on type=%r\n%s', remote, sock.getsockname(), stack.transport.type, data) if data: try: stack.received(data, remote) except: logger.exception('received') elif sock.type == socket.SOCK_STREAM: conn, remote = yield multitask.accept(sock) if conn: logger.debug('%r=>%r connection type %r', remote, conn.getsockname(), stack.transport.type) if stack.transport.type in ('ws', 'wss'): multitask.add( self._wsreceiver(stack, conn, remote, maxsize)) else: multitask.add( self._tcpreceiver(stack, conn, remote, maxsize)) else: raise ValueError, 'invalid socket type'
def receiveRTCP(self, sock): try: fd = sock.fileno() while True: data, remote = yield multitask.recvfrom(sock, self.maxsize) if self.app: self.app.receivedRTCP(data, remote, self.srcRTCP) except GeneratorExit: pass # terminated except: print 'receive RTCP exception', (sys and sys.exc_info()) try: os.close(fd) except: pass
def relayreceiver(sock, fivetuple): while True: # start the main listening loop of the udp server try: data, remote = (yield multitask.recvfrom(sock, maxsize)) # receive a packet if data: if _debug: print 'server().recvfrom() from', remote multitask.add(datahandler(sock1, data, remote)) except: # some other socket error, probably sock1 is closed. break if _debug: print 'server() exiting'
def waitonsock(sock): global pending try: while True: yield multitask.recvfrom(sock, 10) for s in pending: multitask.add(execute(s)) pending[:] = [] except StopIteration: raise except: print 'waitonsock', sys.exc_info(), traceback.print_exc()
def udpreceiver(self, maxsize=16386, timeout=None, interval=30): '''A UDP receiver task which also performs network ack.''' while True: data, addr = yield multitask.recvfrom(self.udp, maxsize, timeout=timeout) msg, remote = self.parse(data, addr, self.udp.type) if not msg: continue # ignore invalid messages. TODO: handle non-p2p message if _debug and msg.name[:4] != 'Hash': print self.name, 'udp-received %s=>%s: %r'%(remote.hostport, self.node.hostport, msg) if 'ack' in msg and msg.name != 'Ack:Indication': # the remote requires an ack. send one. del msg['ack'] # remove the ack ack = dht.Message(name='Ack:Indication', hash=H(data)) # hash of original received packet yield self.send(msg=ack, node=remote) # send the NetworkAck message msg['remote'] = remote # put remote as an attribute in msg before putting on queue. yield self.put(msg) # put the parsed msg so that other interested party may get it.
def _listener(self): '''Listen for transport messages on the signaling socket. The default maximum packet size to receive is 1500 bytes. The interval argument specifies how often should the sock be checked for close, default is 180 s. This is a generator function and should be invoked as multitask.add(u._listener()).''' self.app.status.append('Listener Generator Initiated') try: while self.sock and self._stack: try: data, remote = (yield multitask.recvfrom(self.sock, self.max_size, timeout=self.interval)) logger.debug('received[%d] from %s\n%s' % (len(data), remote, data)) if self.state != self.FUZZING and self.state != self.FUZZING_COMPETED: self._stack.received(data, remote) else: m = rfc3261_IPv6.Message() try: if self.crash_fuzz == self.WAIT_CRSH_CHK and self.crash_porbe == self.CRASH_PROBE_REC: # If response to fuzz is received # m._parse(data) self.app.fuzzResponse[ self.fuzz_index] = data.split( '\n', 1)[0].replace('\r', '') self.crash_porbe = self.FUZZ_RECV elif self.crash_fuzz == self.WAIT_CRSH_CHK and self.crash_porbe == self.CRASH_PROBE_SENT: # If response to probe received self._stack.received(data, remote) self.app.probeResponse[ self.fuzz_index] = data.split( '\n', 1)[0].replace('\r', '') except ValueError, E: # TODO: send 400 response to non-ACK request logger.debug('Error in received message:', E) logger.debug(traceback.print_exc()) except multitask.Timeout: if self.state == self.FUZZING and self.crash_detect: print( bcolors.FAIL + "Crash detected!" + bcolors.ENDC + " It seems that we found a server crash. Server is not responding. If this is a false-positive you can use --fuzz-crash-no-stop option to prevent the app stop at crash detection." ) self.app.printResults() self.app.stop() except GeneratorExit: pass except: print 'User._listener exception', (sys and sys.exc_info() or None) traceback.print_exc() raise logger.debug('terminating User._listener()')
def _listener(self): '''Listen for transport messages on the signaling socket. The interval argument specifies how often should the sock be checked for close, default is 180 s. This is a generator function and should be invoked as multitask.add(u._listener()).''' self.app.status.append('Listener Generator Initiated') try: while self.sock and self._stack: try: data, remote = (yield multitask.recvfrom(self.sock, self.max_size, timeout=self.interval)) logger.debug('received[%d] from %s\n%s'%(len(data),remote,data)) self._stack.received(data, remote) except multitask.Timeout: pass except GeneratorExit: pass except: print 'User._listener exception', (sys and sys.exc_info() or None); traceback.print_exc(); raise logger.debug('terminating User._listener()')
def mcastreceiver(self, maxsize=1500, timeout=None, interval=30): while True: if self.mcast is not None: data, addr = yield multitask.recvfrom(self.mcast, maxsize, timeout=timeout) msg, remote = self.parse(data, addr, self.mcast.type) if not msg: print 'ignoring empty msg'; continue # ignore invalid message. TODO: handle non-p2p message if remote == self.node: if _debug: print 'ignoring our own multicast packet' continue if _debug: print self.name, 'mcast-received %s=>%s: %r'%(remote.hostport, self.nodemcast.hostport, msg) if 'ack' in msg: del msg['ack'] # just remove ack, but don't send an ack for multicast msg['remote'] = remote msg['multicast'] = True # so that application knows that this is received on multicast yield self.put(msg) else: yield dht.randomsleep(interval)
def _sipreceiver(self, stack, maxsize=16386): '''Handle the messages or connections on the given SIP stack's socket, and pass it to the stack so that stack can invoke appropriate callback on this object such as receivedRequest.''' sock = stack.sock while True: if sock.type == socket.SOCK_DGRAM: data, remote = yield multitask.recvfrom(sock, maxsize) logger.debug('%r=>%r on type=%r\n%s', remote, sock.getsockname(), stack.transport.type, data) if data: try: stack.received(data, remote) except: logger.exception('received') elif sock.type == socket.SOCK_STREAM: conn, remote = yield multitask.accept(sock) if conn: logger.debug('%r=>%r connection type %r', remote, conn.getsockname(), stack.transport.type) if stack.transport.type in ('ws', 'wss'): multitask.add(self._wsreceiver(stack, conn, remote, maxsize)) else: multitask.add(self._tcpreceiver(stack, conn, remote, maxsize)) else: raise ValueError, 'invalid socket type'
def udpreceiver(self, maxsize=16386, timeout=None, interval=30): '''A UDP receiver task which also performs network ack.''' while True: data, addr = yield multitask.recvfrom(self.udp, maxsize, timeout=timeout) msg, remote = self.parse(data, addr, self.udp.type) if not msg: continue # ignore invalid messages. TODO: handle non-p2p message if _debug and msg.name[:4] != 'Hash': print self.name, 'udp-received %s=>%s: %r' % ( remote.hostport, self.node.hostport, msg) if 'ack' in msg and msg.name != 'Ack:Indication': # the remote requires an ack. send one. del msg['ack'] # remove the ack ack = dht.Message( name='Ack:Indication', hash=H(data)) # hash of original received packet yield self.send(msg=ack, node=remote) # send the NetworkAck message msg['remote'] = remote # put remote as an attribute in msg before putting on queue. yield self.put( msg ) # put the parsed msg so that other interested party may get it.
def mcastreceiver(self, maxsize=1500, timeout=None, interval=30): while True: if self.mcast is not None: data, addr = yield multitask.recvfrom(self.mcast, maxsize, timeout=timeout) msg, remote = self.parse(data, addr, self.mcast.type) if not msg: print 'ignoring empty msg' continue # ignore invalid message. TODO: handle non-p2p message if remote == self.node: if _debug: print 'ignoring our own multicast packet' continue if _debug: print self.name, 'mcast-received %s=>%s: %r' % ( remote.hostport, self.nodemcast.hostport, msg) if 'ack' in msg: del msg[ 'ack'] # just remove ack, but don't send an ack for multicast msg['remote'] = remote msg['multicast'] = True # so that application knows that this is received on multicast yield self.put(msg) else: yield dht.randomsleep(interval)
def _listener(self): '''Listen for transport messages on the signaling socket. The default maximum packet size to receive is 1500 bytes. The interval argument specifies how often should the sock be checked for close, default is 180 s. This is a generator function and should be invoked as multitask.add(u._listener()).''' self.app.status.append('Listener Generator Initiated') try: while self.sock and self._stack: try: data, remote = (yield multitask.recvfrom(self.sock, self.max_size, timeout=self.interval)) logger.debug('received[%d] from %s\n%s'%(len(data),remote,data)) if self.state != self.FUZZING and self.state != self.FUZZING_COMPETED: self._stack.received(data, remote) else: m = rfc3261_IPv6.Message() try: if self.crash_fuzz==self.WAIT_CRSH_CHK and self.crash_porbe==self.CRASH_PROBE_REC: # If response to fuzz is received # m._parse(data) self.app.fuzzResponse[self.fuzz_index] = data.split('\n', 1)[0].replace('\r','') self.crash_porbe=self.FUZZ_RECV elif self.crash_fuzz==self.WAIT_CRSH_CHK and self.crash_porbe==self.CRASH_PROBE_SENT: # If response to probe received self._stack.received(data, remote) self.app.probeResponse[self.fuzz_index] = data.split('\n', 1)[0].replace('\r','') except ValueError, E: # TODO: send 400 response to non-ACK request logger.debug('Error in received message:', E) logger.debug(traceback.print_exc()) except multitask.Timeout: if self.state == self.FUZZING and self.crash_detect: print (bcolors.FAIL+"Crash detected!"+bcolors.ENDC +" It seems that we found a server crash. Server is not responding. If this is a false-positive you can use --fuzz-crash-no-stop option to prevent the app stop at crash detection.") self.app.printResults() self.app.stop() except GeneratorExit: pass except: print 'User._listener exception', (sys and sys.exc_info() or None); traceback.print_exc(); raise logger.debug('terminating User._listener()')
def _listener(self): '''Listen for transport messages on the signaling socket. The interval argument specifies how often should the sock be checked for close, default is 180 s. This is a generator function and should be invoked as multitask.add(u._listener()).''' self.app.status.append('Listener Generator Initiated') try: while self.sock and self._stack: try: data, remote = (yield multitask.recvfrom(self.sock, self.max_size, timeout=self.interval)) logger.debug('received[%d] from %s\n%s' % (len(data), remote, data)) self._stack.received(data, remote) except multitask.Timeout: pass except GeneratorExit: pass except: print 'User._listener exception', (sys and sys.exc_info() or None) traceback.print_exc() raise logger.debug('terminating User._listener()')
def server(sock1, **kwargs): '''A simple server implementation to test the code or to use in real deployment. The application should start the server as multitask.add(server(sock)). The caller should make sure that the sock1 argument is a UDP or TCP socket which is already bound. Additionally, sock2, sock3, and sock4 can be supplied as keyword arguments and represent the socket to use for change-IP, change-port and change IP+port commands respectively. Other keyword arguments are as follows: timeout: optional acivity timeout (second) if relay is activated, defaults to 180. external: optional external (ip, port) of the socket in case it is behind a full-cone NAT and still acts as a (relay) server. handler: optional function that gets invoked as handler(sock, remote, data) for non-STUN data, and allows the application to demultiplex other types of data. maxsize: optional maximum size of packet to handle, defaults to 1500.''' sock2, sock3, sock4 = kwargs.get('sock2', None), kwargs.get('sock3', None), kwargs.get('sock4', None) addr1 = getlocaladdr(sock1) addr4 = sock4 and getlocaladdr(sock4) or None timeout = kwargs.get('timeout', 180) # three minutes external = kwargs.get('external', addr1) handler = kwargs.get('handler', None) maxsize = kwargs.get('maxsize', 1500) tcp = (sock1.type == socket.SOCK_STREAM) # whether the server is on tcp or udp. binding = dict() # allocated relay bindings if any def respond(sock, data, remote): if sock.type == socket.SOCK_STREAM: yield multitask.send(sock, data) else: yield multitask.sendto(sock, data, remote) def bindingRequest(sock, m, remote): # Serve a binding request of STUN res = Message() res.method, res.type, res.tid = Message.BINDING, Message.RESPONSE, m.tid mapped = Attribute(Attribute.MAPPED_ADDRESS) # mapped-address attribute mapped.address = (sock.family, addr1[0], addr1[1]) res.attrs.append(mapped) if Attribute.CHANGE_REQUEST not in m: # send from same address:port if addr4: # add the other address attribute other = Attribute(Attribute.OTHER_ADDRESS) other.address = (sock4.family, addr4[0], addr4[1]) res.attrs.append(other) else: change = m[Attribute.CHANGE_REQUEST] sock = change.value == '\x00\x00\x00\x06' and sock4 or change.value == '\x00\x00\x00\x02' and sock3 or change.value == '\x00\x00\x00\x04' and sock2 or None if sock: yield respond(sock, str(res), remote) raise StopIteration() def allocateRequest(sock, m, remote): # serve the allocate request of TURN fivetuple = (sock.type, getlocaladdr(sock), remote) lifetime = timeout if Attribute.LIFETIME in m: lt = struct.unpack('!L', m[Attribute.LIFETIME].value) if lt < lifetime: lifetime = lt if fivetuple in binding: # already found newsock = binding[fivetuple] if lifetime == 0: # terminate the binding del binding[fivetuple] del binding[newsock] else: if lifetime > 0: # allocate, otherwise it is already missing. newsock = socket.socket(sock.family, sock.type) newsock.bind(('0.0.0.0', 0)) # bind to any binding[newsock] = fivetuple binding[fivetuple] = newsock res = Message() res.method, res.type, res.tid = m.method, Message.RESPONSE, m.tid mapped = Attribute(Attribute.MAPPED_ADDRESS) # mapped-address attribute mapped.address = (newsock.family, (external, newsock and newsock.getsockname()[1] or 0)) res.attrs.append(mapped) res.attrs.append(Attribute(Attribute.LIFETIME, struct.pack('!L', lifetime))) if lifetime == 0 and newsock: # close any previous listening function newsock.close() # this should trigger close of functions else: if sock.type == socket.SOCK_STREAM: multitask.add(relayaccepter(newsock, fivetuple)) else: multitask.add(relayreceiver(newsock, fivetuple)) yield respond(sock, str(res), remote) def relaytcpreceiver(sock, fivetuple): pass def relayaccepter(sock, fivetuple): sock.listen(5) # accept queue while True: # start the main listening loop of the tcp server try: conn, remote = (yield multitask.accept(sock)) if conn: if _debug: print 'relayaccepter().accept() from', remote sock.close() # close the original listening socket -- no more connections binding[fivetuple] = conn # update the binding del binding[sock] binding[conn] = fivetuple multitask.add(relaytcpreceiver(conn, fivetuple, remote)) break except: # some other socket error, probably sock is closed. break if _debug: print 'relaytcpaccepter() exiting' def relayreceiver(sock, fivetuple): while True: # start the main listening loop of the udp server try: data, remote = (yield multitask.recvfrom(sock, maxsize)) # receive a packet if data: if _debug: print 'server().recvfrom() from', remote multitask.add(datahandler(sock1, data, remote)) except: # some other socket error, probably sock1 is closed. break if _debug: print 'server() exiting' def sendRequest(sock, m, remote): # serve the send request of TURN fivetuple = (sock.type, getlocaladdr(sock), remote) try: if fivetuple not in binding: # not found raise ValueError, 'no turn binding found' newsock = binding[fivetuple] destaddr = Attribute.DESTINATION_ADDRESS in m and m[Attribute.DESTINATION_ADDRESS].address[1:] or None data = Attribute.DATA in m and m[Attribute.DATA] or None if sock.type == socket.SOCK_STREAM: try: remote = newsock.getpeername() except: remote = None if not remote: newsock.connect(destaddr) remote = destaddr yield multitask.send(newsock, data) else: yield multitask.sendto(newsock, data, destaddr) # TODO: we don't lock to destaddr. This is a security risk. result = True except: if _debug: print 'sendRequest() exception', sys.exc_info() result = False res = Message() res.method, res.type, res.tid = m.method, (result and Message.RESPONSE or Message.ERROR), m.tid if not result: error = Attribute(Attribute.ERROR_CODE) error.error = (400, 'cannot send request') # TODO: be more explicit. res.attrs.append(error) yield respond(sock, str(res), remote) def datahandler(sock, data, remote): #handle a new data from given remote (ip, port) try: m = Message(data) # parse the message func = m.type == Message.REQUEST and ( \ m.method == Message.BINDING and bindingRequest \ or m.method == Message.ALLOCATE and allocateRequest \ or m.method == Message.SEND and sendRequest \ ) or None if func: yield func(sock, m, remote) else: raise ValueError, 'unhandled request or message' except StopIteration: if _debug: print 'datahandler: stop iteration' raise except: # parsing error or unhandled message if _debug: print 'datahandler() exception', sys.exc_info() if handler: handler(sock, remote, data) # invoke the application's handler. def tcpreceiver(sock, remote): # handle a new incoming TCP connection while True: data = (yield multitask.recv(sock, maxsize)) if not data: break # socket closed type, length, magic = struct.unpack('!HHL', data[:8]) valid = (type & 0xC000 == 0) and magic == Message.MAGIC and length<=(maxsize-8) # valid if valid: yield datahandler(sock, data, remote) if _debug: print 'tcpreceiver() finished data handler' else: handler(sock, data, remote) if tcp: sock1.listen(5) # create the listen queue if _debug: print 'server listening on', addr1 while True: # start the main listening loop of the server try: tcp = (sock1.type == socket.SOCK_STREAM) except: break # probably a bad file descriptor because sock1 is closed. try: if tcp: conn, remote = (yield multitask.accept(sock1, timeout=5)) if conn: if _debug: print 'server().accept() from', remote multitask.add(tcpreceiver(conn, remote)) else: data, remote = (yield multitask.recvfrom(sock1, maxsize, timeout=5)) # receive a packet if data: if _debug: print 'server().recvfrom() from', remote multitask.add(datahandler(sock1, data, remote)) except multitask.Timeout: continue except: # some other socket error, probably sock1 is closed. break if _debug: print 'server() exiting'
def request(sock, server=None, **kwargs): '''Send a STUN client request with retransmissions and return the response. This is a generator function, and can be called as response, external = yield request(sock, ('stun.iptel.org', 3478)) It raises ValueError in case of failure and multitask.Timeout in case of timeout or failure to connect TCP or invalid response received. For TCP, the sock remains connected after successful return or exception from this function. Arguments are as follows: sock: the socket to use for sending request and receiving response. server: optional server (ip, port), defaults to defaultServers[0]. For TCP if sock is already connected, then server argument is ignored. method: optional STUN method, defaults to Message.BINDING. tid: optional transaction id, by default generates a new. attrs: optional attributes, by default empty list []. rto: optional RTO, defaults to 0.1 for UDP and 9.3 for TCP. retry: optional retry count, defaults to 7 for UDP and 1 for TCP. maxsize: optional maximum packet size, defaults to 1500. handler: optional handler function, that receives any message that was received but not handled by the request method. The handler argument allows demultiplexing other types of received messages on the same socket. Note that raising an exception is not good, because we still want to wait for response instead of exiting. The handler is invoked as handler(sock, remote, data) where data is raw data string and remote is usually server (ip, port). If no handler is specified, then invalid data raises a ValueError. ''' server = server or defaultServers[0] # use first server if missing handler = kwargs.get('handler', None) maxsize = kwargs.get('maxsize', 1500) m = Message() m.method = kwargs.get('method', Message.BINDING) m.type = Message.REQUEST m.tid = kwargs.get('tid', urandom(12)) m.attrs = kwargs.get('attrs', []) mstr = str(m) # formatted message bytes to send if len(mstr) >= maxsize: raise ValueError, 'Cannot send packet of length>%d'%(maxsize) if sock.type == socket.SOCK_STREAM: remote = None try: remote = sock.getpeername() except: pass if not remote: try: sock.connect(server) remote = server # connect if not already connected. except: raise multitask.Timeout() # can't connect, then raise a timeout error. tcp, rto, retry = True, kwargs.get('rto', 9.3), kwargs.get('retry', 1) else: tcp, rto, retry = False, kwargs.get('rto', 0.100), kwargs.get('retry', 7) while retry>0: retry = retry - 1 if _debug: print 'sending STUN request method=%d, len=%d, remaining-retry=%d'%(m.method, len(mstr), retry) if tcp: yield multitask.send(sock, mstr) # send the request else: yield multitask.sendto(sock, mstr, server) try: if tcp: # receiving a TCP packet is complicated. remote is already set data = (yield multitask.recv(sock, maxsize, timeout=rto)) if not data: break if _debug: print 'request() received data' type, length, magic = struct.unpack('!HHL', data[:8]) if type & 0xC000 != 0 or magic != Message.MAGIC: raise ValueError, 'invalid STUN response from server type=0x%x, magic=0x%x'%(type, magic) if length > (maxsize-8): raise ValueError, 'very large response length[%d]>%d'%(length+8, maxsize) else: # receive a UDP datagram data, remote = (yield multitask.recvfrom(sock, maxsize, timeout=rto)) if data: try: response = Message(data) # parse the message if any if _debug: print 'received STUN message method=%d, type=%d'%(response.method, response.type) except: if _debug: print 'received invalid STUN message len=%d'%(len(response)) if handler: handler(sock, remote, data) # allow app to demultiplex continue # retry next else: raise ValueError, 'Invalid response from server' if response.tid != m.tid: if _debug: print 'The tid does not match. ignoring' if handler: handler(sock, remote, data) continue # probably a old response, don't raise exception. external = None for attr in response.attrs: if not attr.optional and attr.type not in Attribute.knownTypes: raise ValueError, 'Attribute 0x%04x not understood in response'%attr.type if response.type == Message.RESPONSE: # success response for attr in response.attrs: if m.method == Message.BINDING: if attr.type == Attribute.XOR_MAPPED_ADDRESS: external = attr.xorAddress # (family, ip, port) elif attr.type == Attribute.MAPPED_ADDRESS: # for backward compatibility with RFC 3489 external = attr.address elif response.type == Message.ERROR: # error response error = None for attr in response.attrs: if attrs.type == Attribute.ERROR_CODE: error = attrs.error # (code, reason) break raise ValueError, 'Request failed with error %r'%error if external: external = external[1:] # ignore the address family raise StopIteration(response, external) # result to the caller # TODO: else do we continue or raise an error? except multitask.Timeout: rto = rto * 2 # double the rto except StopIteration: if _debug: print 'request() returning external=' + str(external) raise except: # any other exception, fall back to Timeout exception if _debug: print 'Some ValueError exception', sys.exc_info() break raise multitask.Timeout # no response after all retransmissions