def __init__(self, app, **kwargs): '''Start an RTP session for the given network with additional optional keyword arguments such as pt, rate, bandwidth, fraction, member, ssrc, cname, seq0, ts0. @param pt: the optional payload type, default 96. @param rate: the optional sampling rate, default 8000. @param bandwidth: the optional total session bandwidth, default 64000. @param fraction: the optional fraction to use for RTCP, default 0.05. @param member: the optional Source object for this member, default constructs a new. @param ssrc: if member is absent, then optional SSRC for Source, default a random number. @param cname: if member is absent, then optional CNAME for Source, default is ssrc@hostname. @param seq0: the optional initial sequence number, default a random number. @param ts0: the optional initial timestamp, default a random number. ''' self.app, self.net, self.pt, self.rate, self.bandwidth, self.fraction, self.member = \ app, None, kwargs.get('pt', 96), kwargs.get('rate', 8000), kwargs.get('bandwidth', 64000), kwargs.get('fraction', 0.05), kwargs.get('member', None) if not self.member: ssrc = kwargs.get('ssrc', random.randint(0, 2**32)) cname = kwargs.get('cname', '%d@%s' % (ssrc, getlocaladdr())) self.member = Source(ssrc=ssrc, items=[(RTCP.CNAME, cname)]) self.seq0, self.ts0 = kwargs.get('seq0', self.randint(0, 2**16)), kwargs.get( 'ts0', self.randint(0, 2**32)) self.seq = self.ts = self.ts1 = 0 # recent seq and ts. ts1 is base time. self.ntp = self.ntp1 = self.tc # recent NTP time and base time. self.rtpsent = self.rtcpsent = self.byesent = self.running = False # @implements RFC3550 P29L1-P29L34 self.tp = self.tn = 0 # tp=last RTCP transmit time, tc=current time, tn=next RTCP scheduled time self.members, self.senders = dict(), dict( ) # TODO: this should be a smart set+map data structure self.pmembers = 0 self.rtcpbw = self.bandwidth * self.fraction self.wesent, self.initial, self.avgrtcpsize = False, True, 200
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 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 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 __init__(self, sock, secure=False): addr = getlocaladdr(sock) self.host, self.port = addr[0], addr[1] print "host: "+ self.host + " port: " + self.port self.type = (sock.type == socket.SOCK_DGRAM and 'udp' or 'tcp') self.secure = secure self.reliable = self.congestionControlled = (sock.type==socket.SOCK_STREAM)
def __init__(self, app, **kwargs): '''Start an RTP session for the given network with additional optional keyword arguments such as pt, rate, bandwidth, fraction, member, ssrc, cname, seq0, ts0. @param pt: the optional payload type, default 96. @param rate: the optional sampling rate, default 8000. @param bandwidth: the optional total session bandwidth, default 64000. @param fraction: the optional fraction to use for RTCP, default 0.05. @param member: the optional Source object for this member, default constructs a new. @param ssrc: if member is absent, then optional SSRC for Source, default a random number. @param cname: if member is absent, then optional CNAME for Source, default is ssrc@hostname. @param seq0: the optional initial sequence number, default a random number. @param ts0: the optional initial timestamp, default a random number. ''' self.app, self.net, self.pt, self.rate, self.bandwidth, self.fraction, self.member = \ app, None, kwargs.get('pt', 96), kwargs.get('rate', 8000), kwargs.get('bandwidth', 64000), kwargs.get('fraction', 0.05), kwargs.get('member', None) if not self.member: ssrc = kwargs.get('ssrc', random.randint(0, 2**32)) cname = kwargs.get('cname', '%d@%s'%(ssrc, getlocaladdr())) self.member = Source(ssrc=ssrc, items=[(RTCP.CNAME, cname)]) self.seq0, self.ts0 = kwargs.get('seq0', self.randint(0, 2**16)), kwargs.get('ts0', self.randint(0, 2**32)) self.seq = self.ts = self.ts1 = 0 # recent seq and ts. ts1 is base time. self.ntp = self.ntp1 = self.tc # recent NTP time and base time. self.rtpsent = self.rtcpsent = self.byesent = self.running = False # @implements RFC3550 P29L1-P29L34 self.tp = self.tn = 0 # tp=last RTCP transmit time, tc=current time, tn=next RTCP scheduled time self.members, self.senders = dict(), dict() # TODO: this should be a smart set+map data structure self.pmembers = 0 self.rtcpbw = self.bandwidth*self.fraction self.wesent, self.initial, self.avgrtcpsize = False, True, 200
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 _testServer(): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) sock.bind(("0.0.0.0", 0)) # should use any port for testing multitask.add(server(sock)) sockaddr = getlocaladdr(sock) multitask.add(_testDiscoverBehavior([sockaddr, defaultServers[0]])) yield multitask.sleep(5) sock.close()
def _testServer(): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) sock.bind(('0.0.0.0', 0)) # should use any port for testing multitask.add(server(sock)) sockaddr = getlocaladdr(sock) multitask.add(_testDiscoverBehavior([sockaddr, defaultServers[0]])) yield multitask.sleep(5) sock.close()
def _testRequest(): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) sock.bind(('0.0.0.0', 0)) try: local = getlocaladdr(sock) response, external = yield request(sock, ('stun.iptel.org', defaultPort)) print 'local=', local, 'external=', external, 'remote=', remote except (ValueError, multitask.Timeout), E: print 'exception - ValueError or Timeout', E
def _testRequest(): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) sock.bind(("0.0.0.0", 0)) try: local = getlocaladdr(sock) response, external = yield request(sock, ("stun.iptel.org", defaultPort)) print "local=", local, "external=", external, "remote=", remote except (ValueError, multitask.Timeout), E: print "exception - ValueError or Timeout", E
def _testRelay(): try: sock1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock1.bind(('0.0.0.0', 0)) multitask.add(server(sock1)) sockaddr = getlocaladdr(sock1) sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock2.bind(('0.0.0.0', 0)) yield multitask.sleep(2) response, mapped = request(sock2, sockaddr, method=Message.ALLOCATE) print 'mapped=', mapped sock1.close() sock2.close() yield multitask.sleep(6) except: print 'exception', sys.exc_info(), traceback.print_exc(file=sys.stdout)
def _testTcpRequest(): try: sock1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock1.bind(('0.0.0.0', 0)) # should use any port for testing multitask.add(server(sock1)) sockaddr = getlocaladdr(sock1) sock2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock2.bind(('0.0.0.0', 0)) yield multitask.sleep(2) # wait for server to be started. response, external = (yield request(sock2, sockaddr)) print 'external=', external sock1.close() yield multitask.sleep(6) print '_testTcpRequest() exiting' except (ValueError, multitask.Timeout), E: print 'exception - ValueError or Timeout', E
def _testRelay(): try: sock1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock1.bind(("0.0.0.0", 0)) multitask.add(server(sock1)) sockaddr = getlocaladdr(sock1) sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock2.bind(("0.0.0.0", 0)) yield multitask.sleep(2) response, mapped = request(sock2, sockaddr, method=Message.ALLOCATE) print "mapped=", mapped sock1.close() sock2.close() yield multitask.sleep(6) except: print "exception", sys.exc_info(), traceback.print_exc(file=sys.stdout)
def _testTcpRequest(): try: sock1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock1.bind(("0.0.0.0", 0)) # should use any port for testing multitask.add(server(sock1)) sockaddr = getlocaladdr(sock1) sock2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock2.bind(("0.0.0.0", 0)) yield multitask.sleep(2) # wait for server to be started. response, external = (yield request(sock2, sockaddr)) print "external=", external sock1.close() yield multitask.sleep(6) print "_testTcpRequest() exiting" except (ValueError, multitask.Timeout), E: print "exception - ValueError or Timeout", E
import log # Others: [multitask, helper_functions] sys.path.append(''.join([os.getcwd(), '/lib/'])) import multitask # Signal exit from helper_functions import original_sigint, set_original_sigint, return_original_sigint, bcolors logger = logging.getLogger( 'app') # debug(), info(), warning(), error() and critical() except ImportError: print 'We had a problem importing dependencies.' traceback.print_exc() sys.exit(1) #=================================================================================================================== #------------------------------MAIN(1)[Usage+Options]------------------------------ if __name__ == '__main__': default_ext_ip, default_domain, default_login = kutil.getlocaladdr( )[0], socket.gethostname(), os.getlogin() from optparse import OptionParser, OptionGroup from helper_functions import original_sigint, set_original_sigint, return_original_sigint, bcolors # Import Modules (Usage/Options) from module_flooder import module_Usage as flooder_Usage, module_Options as flooder_Options from module_fuzzer import module_Usage as fuzzer_Usage, module_Options as fuzzer_Options from module_spoofer import module_Usage as spoofer_Usage, module_Options as spoofer_Options # Welcome message print( bcolors.OKGREEN + "===================================================================================================================" + bcolors.ENDC) print(bcolors.OKGREEN + "Welcome to SIPOT test tool." + bcolors.ENDC) print( bcolors.OKGREEN + "==================================================================================================================="
import rfc3550, rfc4566, kutil sys.path.append(''.join([os.getcwd(), '/lib/IPv6_fixes'])) import rfc3261_IPv6, rfc2396_IPv6 sys.path.append(''.join([os.getcwd(), '/lib/39peers/external'])) import log # Others: [multitask, helper_functions] sys.path.append(''.join([os.getcwd(), '/lib/'])) import multitask # Signal exit from helper_functions import original_sigint, set_original_sigint, return_original_sigint, bcolors logger = logging.getLogger('app') # debug(), info(), warning(), error() and critical() except ImportError: print 'We had a problem importing dependencies.'; traceback.print_exc(); sys.exit(1) #=================================================================================================================== #------------------------------MAIN(1)[Usage+Options]------------------------------ if __name__ == '__main__': default_ext_ip, default_domain, default_login = kutil.getlocaladdr()[0], socket.gethostname(), os.getlogin() from optparse import OptionParser, OptionGroup from helper_functions import original_sigint, set_original_sigint, return_original_sigint, bcolors # Import Modules (Usage/Options) from module_flooder import module_Usage as flooder_Usage, module_Options as flooder_Options from module_fuzzer import module_Usage as fuzzer_Usage, module_Options as fuzzer_Options from module_spoofer import module_Usage as spoofer_Usage, module_Options as spoofer_Options # Welcome message print (bcolors.OKGREEN+"==================================================================================================================="+bcolors.ENDC) print (bcolors.OKGREEN+"Welcome to SIPOT test tool."+bcolors.ENDC) print (bcolors.OKGREEN+"==================================================================================================================="+bcolors.ENDC) # Usage ------------------------------ usage = "Usage: %prog [options]" usage += "Examples:\r\n" usage += "Register extention:\r\n" usage += "\tpython %prog --register --username 109 --pwd abc123 --reg-ip 192.168.56.77 \r\n"
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 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"