Example #1
0
    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
Example #2
0
 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)
Example #3
0
 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)
Example #4
0
 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)
Example #5
0
 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)
Example #6
0
 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
Example #7
0
    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)
Example #8
0
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()
Example #9
0
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()
Example #10
0
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
Example #11
0
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
Example #12
0
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)    
Example #13
0
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
Example #14
0
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)
Example #15
0
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
Example #16
0
    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 +
        "==================================================================================================================="
Example #17
0
	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"
Example #18
0
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'
Example #19
0
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"