Example #1
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 #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 sendRTCP(self, data, dest=None):
     if self.rtcp:
         dest = dest or self.destRTCP
         if dest and dest[1] > 0 and dest[0] != '0.0.0.0':
             if _debug: print 'sending RTCP %d to %r' % (len(data), dest)
             yield multitask.sendto(self.rtcp, data, dest)
         elif _debug:
             print 'ignoring send RTCP'
Example #4
0
 def sendRTCP(self, data, dest=None):
     if self.rtcp:
         dest = dest or self.destRTCP
         if dest and dest[1] > 0 and dest[0] != "0.0.0.0":
             if _debug:
                 print "sending RTCP %d to %r" % (len(data), dest)
             yield multitask.sendto(self.rtcp, data, dest)
         elif _debug:
             print "ignoring send RTCP"
Example #5
0
 def _send(self, data, remote, stack): # a generator function that does the sending
     logger.debug('%r=>%r on type=%r\n%s', stack.sock.getsockname(), remote, stack.transport.type, data)
     try:
         if stack.sock.type == socket.SOCK_STREAM: # for TCP send only if a connection exists to the remote.
             if stack.transport.type in ('ws', 'wss'):
                 if len(data) < 126:
                     init = struct.pack('>BB', 0x81, len(data))
                 elif len(data) < 65536:
                     init = struct.pack('>BBH', 0x81, 126, len(data))
                 else:
                     raise ValueError, 'cannot send long message'
                 data = init + data
             if remote in self.conn:
                 yield multitask.send(self.conn[remote], data) # and send using that connected TCP socket.
             else:
                 logger.warning('ignoring message to %r as no existing connection', remote)
         else: # for UDP send using the stack's UDP socket.
             yield multitask.sendto(stack.sock, data, remote)
     except StopIteration: pass
     except: 
         logger.exception('sending')
Example #6
0
 def respond(sock, data, remote):
     if sock.type == socket.SOCK_STREAM:
         yield multitask.send(sock, data)
     else:
         yield multitask.sendto(sock, data, remote)
Example #7
0
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
Example #8
0
 def respond(sock, data, remote):
     if sock.type == socket.SOCK_STREAM:
         yield multitask.send(sock, data)
     else:
         yield multitask.sendto(sock, data, remote)
Example #9
0
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