Пример #1
0
    def __init__(self, server, starting_mid, callback):
        self._currentMID = starting_mid
        self._server = server
        self._callback = callback
        self.stopped = threading.Event()

        self._messageLayer = MessageLayer(self._currentMID)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()
        self._requestLayer = RequestLayer(self)

        # try:
        #     # legal
        #     socket.inet_aton(server[0])
        # except socket.error:
        #     # Not legal
        #     data = socket.getaddrinfo(server[0], server[1])
        #     self._server = (data[0], data[1])

        host, port = self._server
        addrinfo = socket.getaddrinfo(host, None)[0]

        if addrinfo[0] == socket.AF_INET:
            self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        else:
            self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self._receiver_thread = threading.Thread(target=self.receive_datagram)
        self._receiver_thread.daemon = True
        self._receiver_thread.start()
Пример #2
0
    def __init__(self, server_address, multicast=False, starting_mid=None):

        self.stopped = threading.Event()
        self.stopped.clear()
        self.to_be_stopped = []
        self.purge = threading.Thread(target=self.purge)
        self.purge.start()

        host, port = server_address
        ret = socket.getaddrinfo(host, port)
        family, socktype, proto, canonname, sockaddr = ret[0]

        self._messageLayer = MessageLayer(starting_mid)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()

        self._forwardLayer = ForwardLayer(self)
        self.resourceLayer = ResourceLayer(self)

        # Resource directory
        root = Resource('root', self, visible=False, observable=False, allow_children=True)
        root.path = '/'
        self.root = Tree()
        self.root["/"] = root
        self._serializer = None

        self.server_address = server_address
        self.multicast = multicast

        # IPv4 or IPv6
        if len(sockaddr) == 4:
            self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        else:
            self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        if self.multicast:
            # Set some options to make it multicast-friendly
            try:
                    self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
            except AttributeError:
                    pass  # Some systems don't support SO_REUSEPORT
            self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 20)
            self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)

            # Bind to the port
            self._socket.bind(self.server_address)

            # Set some more multicast options
            interface = socket.gethostbyname(socket.gethostname())
            self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(interface))
            self._socket.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(self.server_address)
                                    + socket.inet_aton(interface))
        else:
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

            self._socket.bind(self.server_address)
Пример #3
0
    def __init__(self, server_address, multicast=False, starting_mid=None):

        self.stopped = threading.Event()
        self.stopped.clear()
        self.to_be_stopped = []
        self.purge = threading.Thread(target=self.purge)
        self.purge.start()

        self._messageLayer = MessageLayer(starting_mid)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()

        self._forwardLayer = ForwardLayer(self)
        self.resourceLayer = ResourceLayer(self)

        # Resource directory
        root = Resource('root', self, visible=False, observable=False, allow_children=True)
        root.path = '/'
        self.root = Tree()
        self.root["/"] = root
        self._serializer = None

        self.server_address = server_address
        self.multicast = multicast

        addrinfo = socket.getaddrinfo(self.server_address[0], None)[0]

        if self.multicast: # pragma: no cover

            # Create a socket
            self._socket = socket.socket(addrinfo[1], socket.SOCK_DGRAM)

            # Allow multiple copies of this program on one machine
            # (not strictly needed)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

            # Bind it to the port
            self._socket.bind(('', self.server_address[1]))

            group_bin = socket.inet_pton(addrinfo[1], addrinfo[4][0])
            # Join group
            if addrinfo[0] == socket.AF_INET: # IPv4
                mreq = group_bin + struct.pack('=I', socket.INADDR_ANY)
                self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
            else:
                mreq = group_bin + struct.pack('@I', 0)
                self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)

        else:
            if addrinfo[0] == socket.AF_INET: # IPv4
                self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            else:
                self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

            self._socket.bind(self.server_address)
Пример #4
0
    def __init__(self, server, starting_mid, callback):
        self._currentMID = starting_mid
        self._server = server
        self._callback = callback
        self.stopped = threading.Event()

        self._messageLayer = MessageLayer(self._currentMID)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()
        self._requestLayer = RequestLayer(self)

        try:
            # legal
            socket.inet_aton(server[0])
        except socket.error:
            # Not legal
            data = socket.getaddrinfo(server[0], server[1])
            self._server = (data[0], data[1])

        self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self._receiver_thread = threading.Thread(target=self.receive_datagram)
        self._receiver_thread.start()
Пример #5
0
    def __init__(self, server, starting_mid, callback, sock=None, cb_ignore_read_exception=None, cb_ignore_write_exception=None):
        """
        Initialize the client.

        :param server: Server address for incoming connections
        :param callback:the callback function to be invoked when a response is received
        :param starting_mid: used for testing purposes
        :param sock: if a socket has been created externally, it can be used directly
        :param cb_ignore_read_exception: Callback function to handle exception raised during the socket read operation
        :param cb_ignore_write_exception: Callback function to handle exception raised during the socket write operation        
        """
        self._currentMID = starting_mid
        self._server = server
        self._callback = callback
        self._cb_ignore_read_exception = cb_ignore_read_exception
        self._cb_ignore_write_exception = cb_ignore_write_exception
        self.stopped = threading.Event()
        self.to_be_stopped = []

        self._messageLayer = MessageLayer(self._currentMID)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()
        self._requestLayer = RequestLayer(self)

        addrinfo = socket.getaddrinfo(self._server[0], None)[0]

        if sock is not None:
            self._socket = sock

        elif addrinfo[0] == socket.AF_INET:
            self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        else:
            self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        self._receiver_thread = None
Пример #6
0
class CoAP(object):
    """
    Implementation of the Forward Proxy
    """
    def __init__(self, server_address, multicast=False, starting_mid=None, cache=False, sock=None):
        """
        Initialize the Forward Proxy.

        :param server_address: Server address for incoming connections
        :param multicast: if the ip is a multicast address
        :param starting_mid: used for testing purposes
        :param cache: if a cache must be used
        :param sock: if a socket has been created externally, it can be used directly
        """
        self.stopped = threading.Event()
        self.stopped.clear()
        self.to_be_stopped = []
        self.purge = threading.Thread(target=self.purge)
        self.purge.start()
        self.cache_enable = cache

        self._messageLayer = MessageLayer(starting_mid)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()

        if self.cache_enable:
            self._cacheLayer = CacheLayer(defines.FORWARD_PROXY)
        else:
            self._cacheLayer = None

        self._forwardLayer = ForwardLayer(self)
        self.resourceLayer = ResourceLayer(self)

        # Resource directory
        root = Resource('root', self, visible=False, observable=False, allow_children=True)
        root.path = '/'
        self.root = Tree()
        self.root["/"] = root
        self._serializer = None

        self.server_address = server_address
        self.multicast = multicast

        addrinfo = socket.getaddrinfo(self.server_address[0], None)[0]

        if sock is not None:

            # Use given socket, could be a DTLS socket
            self._socket = sock

        elif self.multicast:  # pragma: no cover

            # Create a socket
            # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255)
            # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)

            # Join group
            if addrinfo[0] == socket.AF_INET:  # IPv4
                self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)

                # Allow multiple copies of this program on one machine
                # (not strictly needed)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                self._socket.bind((defines.ALL_COAP_NODES, self.server_address[1]))
                mreq = struct.pack("4sl", socket.inet_aton(defines.ALL_COAP_NODES), socket.INADDR_ANY)
                self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
                self._unicast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                self._unicast_socket.bind(self.server_address)
            else:
                self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP)

                # Allow multiple copies of this program on one machine
                # (not strictly needed)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                self._socket.bind((defines.ALL_COAP_NODES_IPV6, self.server_address[1]))

                addrinfo_multicast = socket.getaddrinfo(defines.ALL_COAP_NODES_IPV6, 5683)[0]
                group_bin = socket.inet_pton(socket.AF_INET6, addrinfo_multicast[4][0])
                mreq = group_bin + struct.pack('@I', 0)
                self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
                self._unicast_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
                self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                self._unicast_socket.bind(self.server_address)
        else:
            if addrinfo[0] == socket.AF_INET: # IPv4
                self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            else:
                self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

            self._socket.bind(self.server_address)

    def purge(self):
        """
        Clean old transactions
        """
        while not self.stopped.isSet():
            self.stopped.wait(timeout=defines.EXCHANGE_LIFETIME)
            self._messageLayer.purge()

    def listen(self, timeout=10):
        """
        Listen for incoming messages. Timeout is used to check if the server must be switched off.

        :param timeout: Socket Timeout in seconds
        """
        self._socket.settimeout(float(timeout))
        while not self.stopped.isSet():
            try:
                data, client_address = self._socket.recvfrom(4096)
            except socket.timeout:
                continue
            try:
                #Start a new thread not to block other requests
                args = ((data, client_address), )
                t = threading.Thread(target=self.receive_datagram, args=args)
                t.daemon = True
                t.start()
            except RuntimeError:
                logging.exception("Exception with Executor")
        logging.debug("closing socket")
        self._socket.close()

    def close(self):
        """
        Stop the server.

        """
        logger.info("Stop server")
        self.stopped.set()
        for event in self.to_be_stopped:
            event.set()
        self._socket.close()

    def receive_datagram(self, args):
        """
        Handle messages coming from the udp socket.

        :param args: (data, client_address)
        """
        data, client_address = args

        logging.debug("receiving datagram")

        try:
            host, port = client_address
        except ValueError:
            host, port, tmp1, tmp2 = client_address

        client_address = (host, port)
        
        serializer = Serializer()
        message = serializer.deserialize(data, client_address)
        if isinstance(message, int):
            logger.error("receive_datagram - BAD REQUEST")

            rst = Message()
            rst.destination = client_address
            rst.type = defines.Types["RST"]
            rst.code = message
            self.send_datagram(rst)
            return
        logger.debug("receive_datagram - " + str(message))
        if isinstance(message, Request):

            transaction = self._messageLayer.receive_request(message)

            if transaction.request.duplicated and transaction.completed:
                logger.debug("message duplicated,transaction completed")
                transaction = self._observeLayer.send_response(transaction)
                transaction = self._blockLayer.send_response(transaction)
                transaction = self._messageLayer.send_response(transaction)
                self.send_datagram(transaction.response)
                return
            elif transaction.request.duplicated and not transaction.completed:
                logger.debug("message duplicated,transaction NOT completed")
                self._send_ack(transaction)
                return

            transaction.separate_timer = self._start_separate_timer(transaction)

            transaction = self._blockLayer.receive_request(transaction)

            if transaction.block_transfer:
                self._stop_separate_timer(transaction.separate_timer)
                transaction = self._messageLayer.send_response(transaction)
                self.send_datagram(transaction.response)
                return

            transaction = self._observeLayer.receive_request(transaction)

            """
            call to the cache layer to check if there's a cached response for the request
            if not, call the forward layer
            """
            if self._cacheLayer is not None:
                transaction = self._cacheLayer.receive_request(transaction)

                if transaction.cacheHit is False:
                    logging.debug(transaction.request)
                    transaction = self._forwardLayer.receive_request(transaction)
                    logging.debug(transaction.response)

                transaction = self._observeLayer.send_response(transaction)

                transaction = self._blockLayer.send_response(transaction)

                transaction = self._cacheLayer.send_response(transaction)
            else:
                transaction = self._forwardLayer.receive_request(transaction)

                transaction = self._observeLayer.send_response(transaction)

                transaction = self._blockLayer.send_response(transaction)

            self._stop_separate_timer(transaction.separate_timer)

            transaction = self._messageLayer.send_response(transaction)

            if transaction.response is not None:
                if transaction.response.type == defines.Types["CON"]:
                    self._start_retransmission(transaction, transaction.response)
                self.send_datagram(transaction.response)

        elif isinstance(message, Message):
            transaction = self._messageLayer.receive_empty(message)
            if transaction is not None:
                transaction = self._blockLayer.receive_empty(message, transaction)
                self._observeLayer.receive_empty(message, transaction)

        else:  # is Response
            logger.error("Received response from %s", message.source)

    def send_datagram(self, message):
        """
        Send a message through the udp socket.

        :type message: Message
        :param message: the message to send
        """
        if not self.stopped.isSet():
            host, port = message.destination
            logger.debug("send_datagram - " + str(message))
            serializer = Serializer()
            message = serializer.serialize(message)

            self._socket.sendto(message, (host, port))

    def _start_retransmission(self, transaction, message):
        """
        Start the retransmission task.

        :type transaction: Transaction
        :param transaction: the transaction that owns the message that needs retransmission
        :type message: Message
        :param message: the message that needs the retransmission task
        """
        with transaction:
            if message.type == defines.Types['CON']:
                future_time = random.uniform(defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR))
                transaction.retransmit_thread = threading.Thread(target=self._retransmit,
                                                                 args=(transaction, message, future_time, 0))
                transaction.retransmit_stop = threading.Event()
                self.to_be_stopped.append(transaction.retransmit_stop)
                transaction.retransmit_thread.start()

    def _retransmit(self, transaction, message, future_time, retransmit_count):
        """
        Thread function to retransmit the message in the future

        :param transaction: the transaction that owns the message that needs retransmission
        :param message: the message that needs the retransmission task
        :param future_time: the amount of time to wait before a new attempt
        :param retransmit_count: the number of retransmissions
        """
        with transaction:
            while retransmit_count < defines.MAX_RETRANSMIT and (not message.acknowledged and not message.rejected) \
                    and not self.stopped.isSet():
                transaction.retransmit_stop.wait(timeout=future_time)
                if not message.acknowledged and not message.rejected and not self.stopped.isSet():
                    retransmit_count += 1
                    future_time *= 2
                    self.send_datagram(message)

            if message.acknowledged or message.rejected:
                message.timeouted = False
            else:
                logger.warning("Give up on message {message}".format(message=message.line_print))
                message.timeouted = True
                if message.observe is not None:
                    self._observeLayer.remove_subscriber(message)

            try:
                self.to_be_stopped.remove(transaction.retransmit_stop)
            except ValueError:
                pass
            transaction.retransmit_stop = None
            transaction.retransmit_thread = None

    def _start_separate_timer(self, transaction):
        """
        Start a thread to handle separate mode.

        :type transaction: Transaction
        :param transaction: the transaction that is in processing
        :rtype : the Timer object
        """
        t = threading.Timer(defines.ACK_TIMEOUT, self._send_ack, (transaction,))
        t.start()
        return t

    @staticmethod
    def _stop_separate_timer(timer):
        """
        Stop the separate Thread if an answer has been already provided to the client.

        :param timer: The Timer object
        """
        timer.cancel()

    def _send_ack(self, transaction):
        """
        Sends an ACK message for the request.

        :param transaction: the transaction that owns the request
        """

        ack = Message()
        ack.type = defines.Types['ACK']

        if not transaction.request.acknowledged:
            ack = self._messageLayer.send_empty(transaction, transaction.request, ack)
            self.send_datagram(ack)
Пример #7
0
    def __init__(self, server_address, multicast=False, starting_mid=None):

        """
        Initialize the server.

        :param server_address: Server address for incoming connections
        :param multicast: if the ip is a multicast address
        :param starting_mid: used for testing purposes
        """
        self.stopped = threading.Event()
        self.stopped.clear()
        self.to_be_stopped = []
        self.purge = threading.Thread(target=self.purge)
        self.purge.start()

        host, port = server_address
        ret = socket.getaddrinfo(host, port)
        family, socktype, proto, canonname, sockaddr = ret[0]

        self._messageLayer = MessageLayer(starting_mid)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()
        self._requestLayer = RequestLayer(self)
        self.resourceLayer = ResourceLayer(self)

        # Resource directory
        root = Resource("root", self, visible=False, observable=False, allow_children=False)
        root.path = "/"
        self.root = Tree()
        self.root["/"] = root
        self._serializer = None

        self.server_address = server_address
        self.multicast = multicast

        # IPv4 or IPv6
        if len(sockaddr) == 4:
            self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        else:
            self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        if self.multicast:
            self._socket.bind(("", self.server_address[1]))
            try:
                group = socket.inet_aton(self.server_address[0])
            except:
                group = self.server_address[0]
            mreq = struct.pack("4sL", group, socket.INADDR_ANY)
            self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)

            # # Set some options to make it multicast-friendly
            # try:
            #         self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
            # except AttributeError:
            #         pass  # Some systems don't support SO_REUSEPORT
            # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 20)
            # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)
            #
            # # Bind to the port
            # self._socket.bind(self.server_address)
            #
            # # Set some more multicast options
            # interface = socket.gethostbyname(socket.gethostname())
            # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(interface))
            # self._socket.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(self.server_address[0])
            #                         + socket.inet_aton(interface))
        else:
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

            self._socket.bind(self.server_address)
Пример #8
0
    def __init__(self, server_address, multicast=False, starting_mid=None):

        """
        Initialize the server.

        :param server_address: Server address for incoming connections
        :param multicast: if the ip is a multicast address
        :param starting_mid: used for testing purposes
        """
        self.stopped = threading.Event()
        self.stopped.clear()
        self.to_be_stopped = []
        self.purge = threading.Thread(target=self.purge)
        self.purge.start()

        self._messageLayer = MessageLayer(starting_mid)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()
        self._requestLayer = RequestLayer(self)
        self.resourceLayer = ResourceLayer(self)

        # Resource directory
        root = Resource('root', self, visible=False, observable=False, allow_children=False)
        root.path = '/'
        self.root = Tree()
        self.root["/"] = root
        self._serializer = None

        self.server_address = server_address
        self.multicast = multicast

        addrinfo = socket.getaddrinfo(self.server_address[0], None)[0]

        if self.multicast:  # pragma: no cover

            # Create a socket
            self._socket = socket.socket(addrinfo[1], socket.SOCK_DGRAM)

            # Allow multiple copies of this program on one machine
            # (not strictly needed)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

            self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255)
            self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)

            # Bind it to the port
            self._socket.bind(('', self.server_address[1]))

            # Join group
            if addrinfo[0] == socket.AF_INET:  # IPv4
                addrinfo_multicast = socket.getaddrinfo(defines.ALL_COAP_NODES, None)[0]
                group_bin = socket.inet_pton(addrinfo_multicast[1], addrinfo_multicast[4][0])
                mreq = group_bin + struct.pack('=I', socket.INADDR_ANY)
                self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
                self._unicast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                self._unicast_socket.bind(self.server_address)
            else:
                addrinfo_multicast = socket.getaddrinfo(defines.ALL_COAP_NODES_IPV6, None)[0]
                group_bin = socket.inet_pton(addrinfo_multicast[1], addrinfo_multicast[4][0])
                mreq = group_bin + struct.pack('@I', 0)
                self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
                self._unicast_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
                self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                self._unicast_socket.bind(self.server_address)

        else:
            if addrinfo[0] == socket.AF_INET:  # IPv4
                self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            else:
                self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

            self._socket.bind(self.server_address)
Пример #9
0
class CoAP(object):
    def __init__(self, server_address, xml_file, multicast=False, starting_mid=None, cache=False):

        self.stopped = threading.Event()
        self.stopped.clear()
        self.to_be_stopped = []
        self.purge = threading.Thread(target=self.purge)
        self.purge.start()

        self._messageLayer = MessageLayer(starting_mid)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()

        self._forwardLayer = ForwardLayer(self)
        self.resourceLayer = ResourceLayer(self)
        self.cache_enable = cache
        if self.cache_enable:
            self._cacheLayer = CacheLayer(defines.REVERSE_PROXY)
        else:
            self._cacheLayer = None

        # Resource directory
        root = Resource('root', self, visible=False, observable=False, allow_children=True)
        root.path = '/'
        self.root = Tree()
        self.root["/"] = root
        self._serializer = None

        self.server_address = server_address
        self.multicast = multicast
        self.file_xml = xml_file
        self._mapping = {}

        addrinfo = socket.getaddrinfo(self.server_address[0], None)[0]

        if self.multicast: # pragma: no cover

            # Create a socket
            self._socket = socket.socket(addrinfo[1], socket.SOCK_DGRAM)

            # Allow multiple copies of this program on one machine
            # (not strictly needed)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

            # Bind it to the port
            self._socket.bind(('', self.server_address[1]))

            group_bin = socket.inet_pton(addrinfo[1], addrinfo[4][0])
            # Join group
            if addrinfo[0] == socket.AF_INET: # IPv4
                mreq = group_bin + struct.pack('=I', socket.INADDR_ANY)
                self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
            else:
                mreq = group_bin + struct.pack('@I', 0)
                self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)

        else:
            if addrinfo[0] == socket.AF_INET:  # IPv4
                self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            else:
                self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

            self._socket.bind(self.server_address)

            self.parse_config()

    def parse_config(self):
        tree = ElementTree.parse(self.file_xml)
        root = tree.getroot()
        for server in root.findall('server'):
            destination = server.text
            name = server.get("name")
            self.discover_remote(destination, name)

    def discover_remote(self, destination, name):
        assert (isinstance(destination, str))
        if destination.startswith("["):
            split = destination.split("]", 1)
            host = split[0][1:]
            port = int(split[1][1:])
        else:
            split = destination.split(":", 1)
            host = split[0]
            port = int(split[1])
        server = (host, port)
        client = HelperClient(server)
        response = client.discover()
        client.stop()
        self.discover_remote_results(response, name)

    def discover_remote_results(self, response, name):
        host, port = response.source

        if response.code == defines.Codes.CONTENT.number:
            resource = Resource('server', self, visible=True, observable=False, allow_children=True)
            self.add_resource(name, resource)
            self._mapping[name] = (host, port)
            self.parse_core_link_format(response.payload, name, (host, port))
        else:
            logger.error("Server: " + response.source + " isn't valid.")

    def parse_core_link_format(self, link_format, base_path, remote_server):
        while len(link_format) > 0:
            pattern = "<([^>]*)>;"
            result = re.match(pattern, link_format)
            path = result.group(1)
            path = path.split("/")
            path = path[1:][0]
            link_format = link_format[result.end(1) + 2:]
            pattern = "([^<,])*"
            result = re.match(pattern, link_format)
            attributes = result.group(0)
            dict_att = {}
            if len(attributes) > 0:
                attributes = attributes.split(";")
                for att in attributes:
                    a = att.split("=")
                    # TODO check correctness
                    dict_att[a[0]] = a[1]
                link_format = link_format[result.end(0) + 1:]
            # TODO handle observing
            resource = RemoteResource('server', remote_server, path, coap_server=self, visible=True, observable=False,
                                      allow_children=True)
            resource.attributes = dict_att
            self.add_resource(base_path + "/" + path, resource)

        logger.info(self.root.dump())

    def purge(self):
        while not self.stopped.isSet():
            self.stopped.wait(timeout=defines.EXCHANGE_LIFETIME)
            self._messageLayer.purge()

    def listen(self, timeout=10):
        """
        Listen for incoming messages. Timeout is used to check if the server must be switched off.

        :param timeout: Socket Timeout in seconds
        """
        self._socket.settimeout(float(timeout))
        while not self.stopped.isSet():
            try:
                data, client_address = self._socket.recvfrom(4096)
            except socket.timeout:
                continue
            try:

                self.receive_datagram((data, client_address))
            except RuntimeError:
                print "Exception with Executor"
        self._socket.close()

    def close(self):
        """
        Stop the server.

        """
        logger.info("Stop server")
        self.stopped.set()
        for event in self.to_be_stopped:
            event.set()
        self._socket.close()

    def receive_datagram(self, args):
        """
        Receive datagram from the udp socket.

        :rtype : Message
        """
        data, client_address = args

        serializer = Serializer()
        message = serializer.deserialize(data, client_address)
        if isinstance(message, int):
            logger.error("receive_datagram - BAD REQUEST")

            rst = Message()
            rst.destination = client_address
            rst.type = defines.Types["RST"]
            rst.code = message
            self.send_datagram(rst)
            return
        logger.debug("receive_datagram - " + str(message))
        if isinstance(message, Request):

            transaction = self._messageLayer.receive_request(message)

            if transaction.request.duplicated and transaction.completed:
                logger.debug("message duplicated,transaction completed")
                transaction = self._observeLayer.send_response(transaction)
                transaction = self._blockLayer.send_response(transaction)
                transaction = self._messageLayer.send_response(transaction)
                self.send_datagram(transaction.response)
                return
            elif transaction.request.duplicated and not transaction.completed:
                logger.debug("message duplicated,transaction NOT completed")
                self._send_ack(transaction)
                return

            transaction.separate_timer = self._start_separate_timer(transaction)

            transaction = self._blockLayer.receive_request(transaction)

            if transaction.block_transfer:
                self._stop_separate_timer(transaction.separate_timer)
                transaction = self._messageLayer.send_response(transaction)
                self.send_datagram(transaction.response)
                return

            transaction = self._observeLayer.receive_request(transaction)

            """
            call to the cache layer to check if there's a cached response for the request
            if not, call the forward layer
            """
            if self._cacheLayer is not None:
                transaction = self._cacheLayer.receive_request(transaction)

                if transaction.cacheHit is False:
                    print transaction.request
                    transaction = self._forwardLayer.receive_request_reverse(transaction)
                    print transaction.response

                transaction = self._observeLayer.send_response(transaction)

                transaction = self._blockLayer.send_response(transaction)

                transaction = self._cacheLayer.send_response(transaction)
            else:
                transaction = self._forwardLayer.receive_request_reverse(transaction)

                transaction = self._observeLayer.send_response(transaction)

                transaction = self._blockLayer.send_response(transaction)

            self._stop_separate_timer(transaction.separate_timer)

            transaction = self._messageLayer.send_response(transaction)

            if transaction.response is not None:
                if transaction.response.type == defines.Types["CON"]:
                    self._start_retrasmission(transaction, transaction.response)
                self.send_datagram(transaction.response)

        elif isinstance(message, Message):
            transaction = self._messageLayer.receive_empty(message)
            if transaction is not None:
                transaction = self._blockLayer.receive_empty(message, transaction)
                self._observeLayer.receive_empty(message, transaction)

        else:  # pragma: no cover
            logger.error("Received response from %s", message.source)

    def send_datagram(self, message):
        """

        :type message: Message
        :param message:
        """
        if not self.stopped.isSet():
            host, port = message.destination
            logger.debug("send_datagram - " + str(message))
            serializer = Serializer()
            message = serializer.serialize(message)

            self._socket.sendto(message, (host, port))

    def add_resource(self, path, resource):
        """
        Helper function to add resources to the resource directory during server initialization.

        :type resource: Resource
        :param resource:
        """

        assert isinstance(resource, Resource)
        path = path.strip("/")
        paths = path.split("/")
        actual_path = ""
        i = 0
        for p in paths:
            i += 1
            actual_path += "/" + p
            try:
                res = self.root[actual_path]
            except KeyError:
                res = None
            if res is None:
                if len(paths) != i:
                    return False
                resource.path = actual_path
                self.root[actual_path] = resource
        return True

    def _start_retrasmission(self, transaction, message):
        """

        :type transaction: Transaction
        :param transaction:
        :type message: Message
        :param message:
        :rtype : Future
        """
        if message.type == defines.Types['CON']:
            future_time = random.uniform(defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR))
            transaction.retransmit_thread = threading.Thread(target=self._retransmit,
                                                             args=(transaction, message, future_time, 0))
            transaction.retransmit_stop = threading.Event()
            self.to_be_stopped.append(transaction.retransmit_stop)
            transaction.retransmit_thread.start()

    def _retransmit(self, transaction, message, future_time, retransmit_count):
        while retransmit_count < defines.MAX_RETRANSMIT and (not message.acknowledged and not message.rejected) \
                and not self.stopped.isSet():
            transaction.retransmit_stop.wait(timeout=future_time)
            if not message.acknowledged and not message.rejected and not self.stopped.isSet():
                retransmit_count += 1
                future_time *= 2
                self.send_datagram(message)

        if message.acknowledged or message.rejected:
            message.timeouted = False
        else:
            logger.warning("Give up on message {message}".format(message=message.line_print))
            message.timeouted = True
            if message.observe is not None:
                self._observeLayer.remove_subscriber(message)

        try:
            self.to_be_stopped.remove(transaction.retransmit_stop)
        except ValueError:
            pass
        transaction.retransmit_stop = None
        transaction.retransmit_thread = None

    def _start_separate_timer(self, transaction):
        """

        :type transaction: Transaction
        :param transaction:
        :type message: Message
        :param message:
        :rtype : Future
        """
        t = threading.Timer(defines.ACK_TIMEOUT, self._send_ack, (transaction,))
        t.start()
        return t

    @staticmethod
    def _stop_separate_timer(timer):
        """

        :type future: Future
        :param future:
        """
        timer.cancel()

    def _send_ack(self, transaction):
        # Handle separate
        """
        Sends an ACK message for the request.

        :param request: [request, sleep_time] or request
        """

        ack = Message()
        ack.type = defines.Types['ACK']

        if not transaction.request.acknowledged:
            ack = self._messageLayer.send_empty(transaction, transaction.request, ack)
            self.send_datagram(ack)
Пример #10
0
class CoAP(object):
    def __init__(self, server_address, multicast=False, starting_mid=None, cache=False):

        self.stopped = threading.Event()
        self.stopped.clear()
        self.to_be_stopped = []
        self.purge = threading.Thread(target=self.purge)
        self.purge.start()
        self.cache_enable = cache

        self._messageLayer = MessageLayer(starting_mid)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()

        if self.cache_enable:
            self._cacheLayer = CacheLayer(defines.FORWARD_PROXY)
        else:
            self._cacheLayer = None

        self._forwardLayer = ForwardLayer(self)
        self.resourceLayer = ResourceLayer(self)

        # Resource directory
        root = Resource('root', self, visible=False, observable=False, allow_children=True)
        root.path = '/'
        self.root = Tree()
        self.root["/"] = root
        self._serializer = None

        self.server_address = server_address
        self.multicast = multicast

        addrinfo = socket.getaddrinfo(self.server_address[0], None)[0]

        if self.multicast: # pragma: no cover

            # Create a socket
            self._socket = socket.socket(addrinfo[1], socket.SOCK_DGRAM)

            # Allow multiple copies of this program on one machine
            # (not strictly needed)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

            # Bind it to the port
            self._socket.bind(('', self.server_address[1]))

            group_bin = socket.inet_pton(addrinfo[1], addrinfo[4][0])
            # Join group
            if addrinfo[0] == socket.AF_INET: # IPv4
                mreq = group_bin + struct.pack('=I', socket.INADDR_ANY)
                self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
            else:
                mreq = group_bin + struct.pack('@I', 0)
                self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)

        else:
            if addrinfo[0] == socket.AF_INET: # IPv4
                self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            else:
                self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

            self._socket.bind(self.server_address)

    def purge(self):
        while not self.stopped.isSet():
            self.stopped.wait(timeout=defines.EXCHANGE_LIFETIME)
            self._messageLayer.purge()

    def listen(self, timeout=10):
        """
        Listen for incoming messages. Timeout is used to check if the server must be switched off.

        :param timeout: Socket Timeout in seconds
        """
        self._socket.settimeout(float(timeout))
        while not self.stopped.isSet():
            try:
                data, client_address = self._socket.recvfrom(4096)
            except socket.timeout:
                continue
            try:
                self.receive_datagram((data, client_address))
            except RuntimeError:
                print "Exception with Executor"
        print "closing socket"
        self._socket.close()

    def close(self):
        """
        Stop the server.

        """
        logger.info("Stop server")
        self.stopped.set()
        for event in self.to_be_stopped:
            event.set()
        self._socket.close()

    def receive_datagram(self, args):
        """
        Receive datagram from the udp socket.

        :rtype : Message
        """
        data, client_address = args

        print "receiving datagram"

        serializer = Serializer()
        message = serializer.deserialize(data, client_address)
        if isinstance(message, int):
            logger.error("receive_datagram - BAD REQUEST")

            rst = Message()
            rst.destination = client_address
            rst.type = defines.Types["RST"]
            rst.code = message
            self.send_datagram(rst)
            return
        logger.debug("receive_datagram - " + str(message))
        if isinstance(message, Request):

            transaction = self._messageLayer.receive_request(message)

            if transaction.request.duplicated and transaction.completed:
                logger.debug("message duplicated,transaction completed")
                transaction = self._observeLayer.send_response(transaction)
                transaction = self._blockLayer.send_response(transaction)
                transaction = self._messageLayer.send_response(transaction)
                self.send_datagram(transaction.response)
                return
            elif transaction.request.duplicated and not transaction.completed:
                logger.debug("message duplicated,transaction NOT completed")
                self._send_ack(transaction)
                return

            transaction.separate_timer = self._start_separate_timer(transaction)

            transaction = self._blockLayer.receive_request(transaction)

            if transaction.block_transfer:
                self._stop_separate_timer(transaction.separate_timer)
                transaction = self._messageLayer.send_response(transaction)
                self.send_datagram(transaction.response)
                return

            transaction = self._observeLayer.receive_request(transaction)

            """
            call to the cache layer to check if there's a cached response for the request
            if not, call the forward layer
            """
            if self._cacheLayer is not None:
                transaction = self._cacheLayer.receive_request(transaction)

                if transaction.cacheHit is False:
                    print transaction.request
                    transaction = self._forwardLayer.receive_request(transaction)
                    print transaction.response

                transaction = self._observeLayer.send_response(transaction)

                transaction = self._blockLayer.send_response(transaction)

                transaction = self._cacheLayer.send_response(transaction)
            else:
                transaction = self._forwardLayer.receive_request(transaction)

                transaction = self._observeLayer.send_response(transaction)

                transaction = self._blockLayer.send_response(transaction)

            self._stop_separate_timer(transaction.separate_timer)

            transaction = self._messageLayer.send_response(transaction)

            if transaction.response is not None:
                if transaction.response.type == defines.Types["CON"]:
                    self._start_retrasmission(transaction, transaction.response)
                self.send_datagram(transaction.response)

        elif isinstance(message, Message):
            transaction = self._messageLayer.receive_empty(message)
            if transaction is not None:
                transaction = self._blockLayer.receive_empty(message, transaction)
                self._observeLayer.receive_empty(message, transaction)

        else:  # is Response
            logger.error("Received response from %s", message.source)

    def send_datagram(self, message):
        """

        :type message: Message
        :param message:
        """
        if not self.stopped.isSet():
            host, port = message.destination
            logger.debug("send_datagram - " + str(message))
            serializer = Serializer()
            message = serializer.serialize(message)

            self._socket.sendto(message, (host, port))

    def _start_retrasmission(self, transaction, message):
        """

        :type transaction: Transaction
        :param transaction:
        :type message: Message
        :param message:
        :rtype : Future
        """
        if message.type == defines.Types['CON']:
            future_time = random.uniform(defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR))
            transaction.retransmit_thread = threading.Thread(target=self._retransmit,
                                                             args=(transaction, message, future_time, 0))
            transaction.retransmit_stop = threading.Event()
            self.to_be_stopped.append(transaction.retransmit_stop)
            transaction.retransmit_thread.start()

    def _retransmit(self, transaction, message, future_time, retransmit_count):
        while retransmit_count < defines.MAX_RETRANSMIT and (not message.acknowledged and not message.rejected) \
                and not self.stopped.isSet():
            transaction.retransmit_stop.wait(timeout=future_time)
            if not message.acknowledged and not message.rejected and not self.stopped.isSet():
                retransmit_count += 1
                future_time *= 2
                self.send_datagram(message)

        if message.acknowledged or message.rejected:
            message.timeouted = False
        else:
            logger.warning("Give up on message {message}".format(message=message.line_print))
            message.timeouted = True
            if message.observe is not None:
                self._observeLayer.remove_subscriber(message)

        try:
            self.to_be_stopped.remove(transaction.retransmit_stop)
        except ValueError:
            pass
        transaction.retransmit_stop = None
        transaction.retransmit_thread = None

    def _start_separate_timer(self, transaction):
        """

        :type transaction: Transaction
        :param transaction:
        :rtype : Timer
        """
        t = threading.Timer(defines.ACK_TIMEOUT, self._send_ack, (transaction,))
        t.start()
        return t

    @staticmethod
    def _stop_separate_timer(timer):
        """

        :type timer: Timer
        :param timer: The timer object
        """
        timer.cancel()

    def _send_ack(self, transaction):
        # Handle separate
        """
        Sends an ACK message for the request.

        :param transaction: The transaction to be acknowledged
        """

        ack = Message()
        ack.type = defines.Types['ACK']

        if not transaction.request.acknowledged:
            ack = self._messageLayer.send_empty(transaction, transaction.request, ack)
            self.send_datagram(ack)
Пример #11
0
class CoAP(object):
    def __init__(self, server_address, xml_file, multicast=False, starting_mid=None):

        self.stopped = threading.Event()
        self.stopped.clear()
        self.to_be_stopped = []
        self.purge = threading.Thread(target=self.purge)
        self.purge.start()

        host, port = server_address
        ret = socket.getaddrinfo(host, port)
        family, socktype, proto, canonname, sockaddr = ret[0]

        self._messageLayer = MessageLayer(starting_mid)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()

        self._forwardLayer = ForwardLayer(self)
        self.resourceLayer = ResourceLayer(self)

        # Resource directory
        root = Resource('root', self, visible=False, observable=False, allow_children=True)
        root.path = '/'
        self.root = Tree()
        self.root["/"] = root
        self._serializer = None

        self.server_address = server_address
        self.multicast = multicast
        self.file_xml = xml_file
        self._mapping = {}

        # IPv4 or IPv6
        if len(sockaddr) == 4:
            self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        else:
            self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        if self.multicast:
            # Set some options to make it multicast-friendly
            try:
                    self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
            except AttributeError:
                    pass  # Some systems don't support SO_REUSEPORT
            self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 20)
            self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)

            # Bind to the port
            self._socket.bind(self.server_address)

            # Set some more multicast options
            interface = socket.gethostbyname(socket.gethostname())
            self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(interface))
            self._socket.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(self.server_address)
                                    + socket.inet_aton(interface))
        else:
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

            self._socket.bind(self.server_address)

            self.parse_config()

    def parse_config(self):
        tree = ElementTree.parse(self.file_xml)
        root = tree.getroot()
        for server in root.findall('server'):
            destination = server.text
            name = server.get("name")
            self.discover_remote(destination, name)

    def discover_remote(self, destination, name):
        assert (isinstance(destination, str))
        split = destination.split(":", 1)
        host = split[0]
        port = int(split[1])
        server = (host, port)
        client = HelperClient(server)
        response = client.discover()
        client.stop()
        self.discover_remote_results(response, name)

    def discover_remote_results(self, response, name):
        host, port = response.source

        if response.code == defines.Codes.CONTENT.number:
            resource = Resource('server', self, visible=True, observable=False, allow_children=True)
            self.add_resource(name, resource)
            self._mapping[name] = (host, port)
            self.parse_core_link_format(response.payload, name, (host, port))
        else:
            logger.error("Server: " + response.source + " isn't valid.")

    def parse_core_link_format(self, link_format, base_path, remote_server):
        while len(link_format) > 0:
            pattern = "<([^>]*)>;"
            result = re.match(pattern, link_format)
            path = result.group(1)
            path = path.split("/")
            path = path[1:][0]
            link_format = link_format[result.end(1) + 2:]
            pattern = "([^<,])*"
            result = re.match(pattern, link_format)
            attributes = result.group(0)
            dict_att = {}
            if len(attributes) > 0:
                attributes = attributes.split(";")
                for att in attributes:
                    a = att.split("=")
                    # TODO check correctness
                    dict_att[a[0]] = a[1]
                link_format = link_format[result.end(0) + 1:]
            # TODO handle observing
            resource = RemoteResource('server', remote_server, path, coap_server=self, visible=True, observable=False,
                                      allow_children=True)
            resource.attributes = dict_att
            self.add_resource(base_path + "/" + path, resource)

        logger.info(self.root.dump())

    def purge(self):
        while not self.stopped.isSet():
            self.stopped.wait(timeout=defines.EXCHANGE_LIFETIME)
            self._messageLayer.purge()

    def listen(self, timeout=10):
        """
        Listen for incoming messages. Timeout is used to check if the server must be switched off.

        :param timeout: Socket Timeout in seconds
        """
        self._socket.settimeout(float(timeout))
        while not self.stopped.isSet():
            try:
                data, client_address = self._socket.recvfrom(4096)
            except socket.timeout:
                continue
            try:

                self.receive_datagram((data, client_address))
            except RuntimeError:
                print "Exception with Executor"
        self._socket.close()

    def close(self):
        """
        Stop the server.

        """
        logger.info("Stop server")
        self.stopped.set()
        for event in self.to_be_stopped:
            event.set()
        self._socket.close()

    def receive_datagram(self, args):
        """
        Receive datagram from the udp socket.

        :rtype : Message
        """
        data, client_address = args

        serializer = Serializer()
        message = serializer.deserialize(data, client_address)
        logger.debug("receive_datagram - " + str(message))
        if isinstance(message, Request):

            transaction = self._messageLayer.receive_request(message)

            if transaction.request.duplicated and transaction.completed:
                logger.debug("message duplicated,transaction completed")
                transaction = self._observeLayer.send_response(transaction)
                transaction = self._blockLayer.send_response(transaction)
                transaction = self._messageLayer.send_response(transaction)
                self.send_datagram(transaction.response)
                return
            elif transaction.request.duplicated and not transaction.completed:
                logger.debug("message duplicated,transaction NOT completed")
                self._send_ack(transaction)
                return

            transaction.separate_timer = self._start_separate_timer(transaction)

            transaction = self._blockLayer.receive_request(transaction)

            if transaction.block_transfer:
                self._stop_separate_timer(transaction.separate_timer)
                transaction = self._messageLayer.send_response(transaction)
                self.send_datagram(transaction.response)
                return

            transaction = self._observeLayer.receive_request(transaction)

            transaction = self._forwardLayer.receive_request_reverse(transaction)

            if transaction.resource is not None and transaction.resource.changed:
                self.notify(transaction.resource)
                transaction.resource.changed = False
            elif transaction.resource is not None and transaction.resource.deleted:
                self.notify(transaction.resource)
                transaction.resource.deleted = False

            transaction = self._observeLayer.send_response(transaction)

            transaction = self._blockLayer.send_response(transaction)

            self._stop_separate_timer(transaction.separate_timer)

            transaction = self._messageLayer.send_response(transaction)

            if transaction.response is not None:
                if transaction.response.type == defines.Types["CON"]:
                    self._start_retrasmission(transaction, transaction.response)
                self.send_datagram(transaction.response)

        elif isinstance(message, Message):
            transaction = self._messageLayer.receive_empty(message)
            if transaction is not None:
                transaction = self._blockLayer.receive_empty(message, transaction)
                transaction = self._observeLayer.receive_empty(message, transaction)

        else:  # is Response
            logger.error("Received response from %s", message.source)

    def send_datagram(self, message):
        """

        :type message: Message
        :param message:
        """
        if not self.stopped.isSet():
            host, port = message.destination
            logger.debug("send_datagram - " + str(message))
            serializer = Serializer()
            message = serializer.serialize(message)

            self._socket.sendto(message, (host, port))

    def add_resource(self, path, resource):
        """
        Helper function to add resources to the resource directory during server initialization.

        :type resource: Resource
        :param resource:
        """

        assert isinstance(resource, Resource)
        path = path.strip("/")
        paths = path.split("/")
        actual_path = ""
        i = 0
        for p in paths:
            i += 1
            actual_path += "/" + p
            try:
                res = self.root[actual_path]
            except KeyError:
                res = None
            if res is None:
                if len(paths) != i:
                    return False
                resource.path = actual_path
                self.root[actual_path] = resource
        return True

    def _start_retrasmission(self, transaction, message):
        """

        :type transaction: Transaction
        :param transaction:
        :type message: Message
        :param message:
        :rtype : Future
        """
        if message.type == defines.Types['CON']:
            future_time = random.uniform(defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR))
            transaction.retransmit_thread = threading.Thread(target=self._retransmit,
                                                             args=(transaction, message, future_time, 0))
            transaction.retransmit_stop = threading.Event()
            self.to_be_stopped.append(transaction.retransmit_stop)
            transaction.retransmit_thread.start()

    def _retransmit(self, transaction, message, future_time, retransmit_count):
        while retransmit_count < defines.MAX_RETRANSMIT and (not message.acknowledged and not message.rejected) \
                and not self.stopped.isSet():
            transaction.retransmit_stop.wait(timeout=future_time)
            if not message.acknowledged and not message.rejected and not self.stopped.isSet():
                retransmit_count += 1
                future_time *= 2
                self.send_datagram(message)

        if message.acknowledged or message.rejected:
            message.timeouted = False
        else:
            logger.warning("Give up on message {message}".format(message=message.line_print))
            message.timeouted = True
            if message.observe is not None:
                self._observeLayer.remove_subscriber(message)

        try:
            self.to_be_stopped.remove(transaction.retransmit_stop)
        except ValueError:
            pass
        transaction.retransmit_stop = None
        transaction.retransmit_thread = None

    def _start_separate_timer(self, transaction):
        """

        :type transaction: Transaction
        :param transaction:
        :type message: Message
        :param message:
        :rtype : Future
        """
        t = threading.Timer(defines.ACK_TIMEOUT, self._send_ack, (transaction,))
        t.start()
        return t

    @staticmethod
    def _stop_separate_timer(timer):
        """

        :type future: Future
        :param future:
        """
        if not timer.finished:
            timer.cancel()

    def _send_ack(self, transaction):
        # Handle separate
        """
        Sends an ACK message for the request.

        :param request: [request, sleep_time] or request
        """

        ack = Message()
        ack.type = defines.Types['ACK']

        if not transaction.request.acknowledged:
            ack = self._messageLayer.send_empty(transaction, transaction.request, ack)
            self.send_datagram(ack)

    def notify(self, resource):
        observers = self._observeLayer.notify(resource)
        logger.debug("Notify")
        for transaction in observers:
            transaction.response = None
            transaction = self._requestLayer.receive_request(transaction)
            transaction = self._observeLayer.send_response(transaction)
            transaction = self._blockLayer.send_response(transaction)
            transaction = self._messageLayer.send_response(transaction)
            if transaction.response is not None:
                if transaction.response.type == defines.Types["CON"]:
                    self._start_retrasmission(transaction, transaction.response)

                self.send_datagram(transaction.response)
Пример #12
0
    def __init__(self, server_address, multicast=False, starting_mid=None, sock=None, cb_ignore_listen_exception=None):
        """
        Initialize the server.

        :param server_address: Server address for incoming connections
        :param multicast: if the ip is a multicast address
        :param starting_mid: used for testing purposes
        :param sock: if a socket has been created externally, it can be used directly
        :param cb_ignore_listen_exception: Callback function to handle exception raised during the socket listen operation
        """
        self.stopped = threading.Event()
        self.stopped.clear()
        self.to_be_stopped = []
        self.purge = threading.Thread(target=self.purge)
        self.purge.start()

        self._messageLayer = MessageLayer(starting_mid)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()
        self._requestLayer = RequestLayer(self)
        self.resourceLayer = ResourceLayer(self)

        # Resource directory
        root = Resource('root', self, visible=False, observable=False, allow_children=False)
        root.path = '/'
        self.root = Tree()
        self.root["/"] = root
        self._serializer = None

        self.server_address = server_address
        self.multicast = multicast
        self._cb_ignore_listen_exception = cb_ignore_listen_exception

        addrinfo = socket.getaddrinfo(self.server_address[0], None)[0]

        if sock is not None:

            # Use given socket, could be a DTLS socket
            self._socket = sock

        elif self.multicast:  # pragma: no cover

            # Create a socket
            # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255)
            # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)

            # Join group
            if addrinfo[0] == socket.AF_INET:  # IPv4
                self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)

                # Allow multiple copies of this program on one machine
                # (not strictly needed)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                self._socket.bind((defines.ALL_COAP_NODES, self.server_address[1]))
                mreq = struct.pack("4sl", socket.inet_aton(defines.ALL_COAP_NODES), socket.INADDR_ANY)
                self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
                self._unicast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                self._unicast_socket.bind(self.server_address)
            else:
                self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP)

                # Allow multiple copies of this program on one machine
                # (not strictly needed)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                self._socket.bind((defines.ALL_COAP_NODES_IPV6, self.server_address[1]))

                addrinfo_multicast = socket.getaddrinfo(defines.ALL_COAP_NODES_IPV6, 5683)[0]
                group_bin = socket.inet_pton(socket.AF_INET6, addrinfo_multicast[4][0])
                mreq = group_bin + struct.pack('@I', 0)
                self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
                self._unicast_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
                self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                self._unicast_socket.bind(self.server_address)

        else:
            if addrinfo[0] == socket.AF_INET:  # IPv4
                self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            else:
                self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

            self._socket.bind(self.server_address)
class CoAP(object):
    def __init__(self, server_address, multicast=False, starting_mid=None):
        """
        Initialize the server.

        :param server_address: Server address for incoming connections
        :param multicast: if the ip is a multicast address
        :param starting_mid: used for testing purposes
        """
        self.stopped = threading.Event()
        self.stopped.clear()
        self.to_be_stopped = []
        self.purge = threading.Thread(target=self.purge)
        self.purge.start()

        self._messageLayer = MessageLayer(starting_mid)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()
        self._requestLayer = RequestLayer(self)
        self.resourceLayer = ResourceLayer(self)

        # Resource directory
        root = Resource('root',
                        self,
                        visible=False,
                        observable=False,
                        allow_children=True)
        root.path = '/'
        self.root = Tree()
        self.root["/"] = root
        self._serializer = None

        self.server_address = server_address
        self.multicast = multicast
        addrinfo = socket.getaddrinfo(self.server_address[0], None)[0]

        if self.multicast:  # pragma: no cover

            # Create a socket
            self._socket = socket.socket(addrinfo[1], socket.SOCK_DGRAM)

            # Allow multiple copies of this program on one machine
            # (not strictly needed)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

            # Bind it to the port
            self._socket.bind(('', self.server_address[1]))

            group_bin = socket.inet_pton(addrinfo[1], addrinfo[4][0])
            # Join group
            if addrinfo[0] == socket.AF_INET:  # IPv4
                mreq = group_bin + struct.pack('=I', socket.INADDR_ANY)
                self._socket.setsockopt(socket.IPPROTO_IP,
                                        socket.IP_ADD_MEMBERSHIP, mreq)
            else:
                mreq = group_bin + struct.pack('@I', 0)
                self._socket.setsockopt(socket.IPPROTO_IPV6,
                                        socket.IPV6_JOIN_GROUP, mreq)

        else:
            if addrinfo[0] == socket.AF_INET:  # IPv4
                self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                        1)
            else:
                self._socket = socket.socket(socket.AF_INET6,
                                             socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                        1)

            self._socket.bind(self.server_address)

    def purge(self):
        """
        Clean old transactions

        """
        while not self.stopped.isSet():
            self.stopped.wait(timeout=defines.EXCHANGE_LIFETIME)
            self._messageLayer.purge()

    def listen(self, timeout=10):
        """
        Listen for incoming messages. Timeout is used to check if the server must be switched off.

        :param timeout: Socket Timeout in seconds
        """

        self._socket.settimeout(float(timeout))
        while not self.stopped.isSet():
            try:
                data, client_address = self._socket.recvfrom(4096)
                if len(client_address) > 2:
                    client_address = (client_address[0], client_address[1])
            except socket.timeout:
                continue
            try:
                serializer = Serializer()
                message = serializer.deserialize(data, client_address)
                if isinstance(message, int):
                    logger.error("receive_datagram - BAD REQUEST")

                    rst = Message()
                    rst.destination = client_address
                    rst.type = defines.Types["RST"]
                    rst.code = message
                    rst.mid = self._messageLayer._current_mid
                    self._messageLayer._current_mid += 1 % 65535
                    self.send_datagram(rst)
                    continue

                logger.debug("receive_datagram - " + str(message))
                if isinstance(message, Request):
                    #logger.debug("send_datagram - " + str(message))
                    #print("ddddddddddddddd:  " + str(message))

                    transaction = self._messageLayer.receive_request(message)
                    if transaction.request.duplicated and transaction.completed:
                        logger.debug(
                            "message duplicated, transaction completed")
                        if transaction.response is not None:
                            self.send_datagram(transaction.response)
                        continue
                    elif transaction.request.duplicated and not transaction.completed:
                        logger.debug(
                            "message duplicated, transaction NOT completed")
                        self._send_ack(transaction)
                        continue

                    args = (transaction, )
                    t = threading.Thread(target=self.receive_request,
                                         args=args)
                    t.start()

                # self.receive_datagram(data, client_address)
                elif isinstance(message, Response):
                    logger.error("Received response from %s", message.source)

                else:  # is Message
                    transaction = self._messageLayer.receive_empty(message)
                    if transaction is not None:
                        with transaction:
                            self._blockLayer.receive_empty(
                                message, transaction)
                            self._observeLayer.receive_empty(
                                message, transaction)

            except RuntimeError:
                print "Exception with Executor"

        self._socket.close()

    def close(self):
        """
        Stop the server.

        """
        logger.info("Stop server")
        self.stopped.set()
        for event in self.to_be_stopped:
            event.set()
        self._socket.close()

    def receive_request(self, transaction):
        """
        Receive datagram from the udp socket.

        :param data: the udp message
        :param client_address: the ip and port of the client
        """

        with transaction:

            transaction.separate_timer = self._start_separate_timer(
                transaction)

            self._blockLayer.receive_request(transaction)

            if transaction.block_transfer:
                self._stop_separate_timer(transaction.separate_timer)
                self._messageLayer.send_response(transaction)
                self.send_datagram(transaction.response)
                return

            self._observeLayer.receive_request(transaction)

            self._requestLayer.receive_request(transaction)

            if transaction.resource is not None and transaction.resource.changed:
                self.notify(transaction.resource)
                transaction.resource.changed = False
            elif transaction.resource is not None and transaction.resource.deleted:
                self.notify(transaction.resource)
                transaction.resource.deleted = False

            self._observeLayer.send_response(transaction)

            self._blockLayer.send_response(transaction)

            self._stop_separate_timer(transaction.separate_timer)

            self._messageLayer.send_response(transaction)

            if transaction.response is not None:
                if transaction.response.type == defines.Types["CON"]:
                    self._start_retransmission(transaction,
                                               transaction.response)
                self.send_datagram(transaction.response)

    def send_datagram(self, message):
        """

        :type message: Message
        :param message:
        """

        if not self.stopped.isSet():

            host, port = message.destination

            logger.debug("send_datagram - " + str(message))

            serializer = Serializer()

            message = serializer.serialize(message)

            self._socket.sendto(message, (host, port))

    def add_resource(self, path, resource):
        """
        Helper function to add resources to the resource directory during server initialization.

        :param path: the path for the new created resource
        :type resource: Resource
        :param resource: the resource to be added
        """

        assert isinstance(resource, Resource)
        path = path.strip("/")
        paths = path.split("/")
        actual_path = ""
        i = 0
        for p in paths:
            i += 1
            actual_path += "/" + p
            try:
                res = self.root[actual_path]
            except KeyError:
                res = None
            if res is None:
                if len(paths) != i:
                    return False
                resource.path = actual_path
                self.root[actual_path] = resource
        return True

    def _start_retransmission(self, transaction, message):
        """
        Start the retransmission task.

        :type transaction: Transaction
        :param transaction: the transaction that owns the message that needs retransmission
        :type message: Message
        :param message: the message that needs the retransmission task
        """
        with transaction:
            if message.type == defines.Types['CON']:
                future_time = random.uniform(
                    defines.ACK_TIMEOUT,
                    (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR))
                transaction.retransmit_thread = threading.Thread(
                    target=self._retransmit,
                    args=(transaction, message, future_time, 0))
                transaction.retransmit_stop = threading.Event()
                self.to_be_stopped.append(transaction.retransmit_stop)
                transaction.retransmit_thread.start()

    def _retransmit(self, transaction, message, future_time, retransmit_count):
        """
        Thread function to retransmit the message in the future

        :param transaction: the transaction that owns the message that needs retransmission
        :param message: the message that needs the retransmission task
        :param future_time: the amount of time to wait before a new attempt
        :param retransmit_count: the number of retransmissions
        """
        with transaction:
            while retransmit_count < defines.MAX_RETRANSMIT and (not message.acknowledged and not message.rejected) \
                    and not self.stopped.isSet():
                transaction.retransmit_stop.wait(timeout=future_time)
                if not message.acknowledged and not message.rejected and not self.stopped.isSet(
                ):
                    retransmit_count += 1
                    future_time *= 2
                    self.send_datagram(message)

            if message.acknowledged or message.rejected:
                message.timeouted = False
            else:
                logger.warning("Give up on message {message}".format(
                    message=message.line_print))
                message.timeouted = True
                if message.observe is not None:
                    self._observeLayer.remove_subscriber(message)

            try:
                self.to_be_stopped.remove(transaction.retransmit_stop)
            except ValueError:
                pass
            transaction.retransmit_stop = None
            transaction.retransmit_thread = None

    def _start_separate_timer(self, transaction):
        """
        Start a thread to handle separate mode.

        :type transaction: Transaction
        :param transaction: the transaction that is in processing
        :rtype : the Timer object
        """
        t = threading.Timer(defines.ACK_TIMEOUT, self._send_ack,
                            (transaction, ))
        t.start()
        return t

    @staticmethod
    def _stop_separate_timer(timer):
        """
        Stop the separate Thread if an answer has been already provided to the client.

        :param timer: The Timer object
        """
        timer.cancel()

    def _send_ack(self, transaction):
        """
        Sends an ACK message for the request.

        :param transaction: the transaction that owns the request
        """

        ack = Message()
        ack.type = defines.Types['ACK']
        # TODO handle mutex on transaction
        if not transaction.request.acknowledged and transaction.request.type == defines.Types[
                "CON"]:
            ack = self._messageLayer.send_empty(transaction,
                                                transaction.request, ack)
            self.send_datagram(ack)

    def notify(self, resource):
        """
        Notifies the observers of a certain resource.

        :param resource: the resource
        """

        observers = self._observeLayer.notify(resource)
        logger.debug("Notify")

        # print resource.observe_client_list
        # print "[[[["

        for transaction in observers:
            with transaction:

                transaction.response = None
                transaction = self._requestLayer.receive_request(transaction)

                transaction = self._observeLayer.send_response(transaction)

                transaction = self._blockLayer.send_response(transaction)

                transaction = self._messageLayer.send_response(transaction)

                print resource.token_address
                if ("introspection" not in resource.__dict__) \
                        or ("introspection" in resource.__dict__
                            and transaction.request.source in resource.observe_client_list):
                    print "OTL"
                    print "OTL"
                    print "OTL"
                    print "OTL"
                    print "OTL"
                    print "OTL"
                    if "introspection" in resource.__dict__ \
                            and transaction.request.source in resource.observe_client_list:
                        resource.observe_client_list.remove(
                            transaction.request.source)
                        print "OTT"
                        print "OTT"
                        print "OTT"
                        print "OTT"
                        print transaction.response.active
                        print transaction.response.active == str(False)
                        if transaction.response.active == str(False):
                            self._observeLayer.remove_subscriber(
                                transaction.response)
                        transaction.response.type = defines.Types["NON"]

                        # option = Option()
                        # option.number = defines.OptionRegistry.BEARER.number
                        # option.value = transaction.request.bearer
                        # transaction.response.add_option(option)
                    if transaction.response is not None:
                        if transaction.response.type == defines.Types["CON"]:
                            self._start_retransmission(transaction,
                                                       transaction.response)

                        self.send_datagram(transaction.response)
Пример #14
0
class CoAP(object):
    def __init__(self, server, starting_mid, callback):
        self._currentMID = starting_mid
        self._server = server
        self._callback = callback
        self.stopped = threading.Event()
        self.to_be_stopped = []

        self._messageLayer = MessageLayer(self._currentMID)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()
        self._requestLayer = RequestLayer(self)

        # try:
        #     # legal
        #     socket.inet_aton(server[0])
        # except socket.error:
        #     # Not legal
        #     data = socket.getaddrinfo(server[0], server[1])
        #     self._server = (data[0], data[1])

        host, port = self._server
        addrinfo = socket.getaddrinfo(host, None)[0]

        if addrinfo[0] == socket.AF_INET:
            self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        else:
            self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self._receiver_thread = threading.Thread(target=self.receive_datagram)
        self._receiver_thread.daemon = True
        self._receiver_thread.start()

    @property
    def current_mid(self):
        return self._currentMID

    @current_mid.setter
    def current_mid(self, c):
        assert isinstance(c, int)
        self._currentMID = c

    def send_message(self, message):

        if isinstance(message, Request):
            request = self._requestLayer.send_request(message)
            request = self._observeLayer.send_request(request)
            request = self._blockLayer.send_request(request)
            transaction = self._messageLayer.send_request(request)
            if transaction.request.type == defines.Types["CON"]:
                self._start_retransmission(transaction, transaction.request)

            self.send_datagram(transaction.request)
        elif isinstance(message, Message):
            message = self._observeLayer.send_empty(message)
            message = self._messageLayer.send_empty(None, None, message)
            self.send_datagram(message)

    def send_datagram(self, message):
        host, port = message.destination
        logger.debug("send_datagram - " + str(message))
        serializer = Serializer()
        message = serializer.serialize(message)

        self._socket.sendto(message, (host, port))

    def _start_retransmission(self, transaction, message):
        """
        Start the retransmission task.

        :type transaction: Transaction
        :param transaction: the transaction that owns the message that needs retransmission
        :type message: Message
        :param message: the message that needs the retransmission task
        """
        with transaction:
            if message.type == defines.Types['CON']:
                future_time = random.uniform(defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR))
                transaction.retransmit_thread = threading.Thread(target=self._retransmit,
                                                                 args=(transaction, message, future_time, 0))
                transaction.retransmit_stop = threading.Event()
                self.to_be_stopped.append(transaction.retransmit_stop)
                transaction.retransmit_thread.start()

    def _retransmit(self, transaction, message, future_time, retransmit_count):
        """
        Thread function to retransmit the message in the future

        :param transaction: the transaction that owns the message that needs retransmission
        :param message: the message that needs the retransmission task
        :param future_time: the amount of time to wait before a new attempt
        :param retransmit_count: the number of retransmissions
        """
        with transaction:
            while retransmit_count < defines.MAX_RETRANSMIT and (not message.acknowledged and not message.rejected) \
                    and not self.stopped.isSet():
                transaction.retransmit_stop.wait(timeout=future_time)
                if not message.acknowledged and not message.rejected and not self.stopped.isSet():
                    logger.debug("retransmit Request")
                    retransmit_count += 1
                    future_time *= 2
                    self.send_datagram(message)

            if message.acknowledged or message.rejected:
                message.timeouted = False
            else:
                logger.warning("Give up on message {message}".format(message=message.line_print))
                message.timeouted = True

            try:
                self.to_be_stopped.remove(transaction.retransmit_stop)
            except ValueError:
                pass
            transaction.retransmit_stop = None
            transaction.retransmit_thread = None

    def receive_datagram(self):
        logger.debug("Start receiver Thread")
        while not self.stopped.isSet():
            self._socket.settimeout(1)
            try:
                datagram, addr = self._socket.recvfrom(1152)
            except socket.timeout:  # pragma: no cover
                continue
            except socket.error:  # pragma: no cover
                return
            else:  # pragma: no cover
                if len(datagram) == 0:
                    print 'orderly shutdown on server end'
                    return

            serializer = Serializer()

            try:
                host, port = addr
            except ValueError:
                host, port, tmp1, tmp2 = addr

            source = (host, port)

            message = serializer.deserialize(datagram, source)

            if isinstance(message, Response):
                transaction, send_ack = self._messageLayer.receive_response(message)
                if transaction is None:  # pragma: no cover
                    continue
                if send_ack:
                    self._send_ack(transaction)
                self._blockLayer.receive_response(transaction)
                if transaction.block_transfer:
                    transaction = self._messageLayer.send_request(transaction.request)
                    self.send_datagram(transaction.request)
                    continue
                elif transaction is None:  # pragma: no cover
                    self._send_rst(transaction)
                    return
                self._observeLayer.receive_response(transaction)
                if transaction.notification:  # pragma: no cover
                    ack = Message()
                    ack.type = defines.Types['ACK']
                    ack = self._messageLayer.send_empty(transaction, transaction.response, ack)
                    self.send_datagram(ack)
                    self._callback(transaction.response)
                else:
                    self._callback(transaction.response)
            elif isinstance(message, Message):
                self._messageLayer.receive_empty(message)

    def _send_ack(self, transaction):
        # Handle separate
        """
        Sends an ACK message for the response.

        :param transaction: transaction that holds the response
        """

        ack = Message()
        ack.type = defines.Types['ACK']

        if not transaction.response.acknowledged:
            ack = self._messageLayer.send_empty(transaction, transaction.response, ack)
            self.send_datagram(ack)

    def _send_rst(self, transaction):  # pragma: no cover
        # Handle separate
        """
        Sends an RST message for the response.

        :param transaction: transaction that holds the response
        """

        rst = Message()
        rst.type = defines.Types['RST']

        if not transaction.response.acknowledged:
            rst = self._messageLayer.send_empty(transaction, transaction.response, rst)
            self.send_datagram(rst)
Пример #15
0
class CoAP(object):
    def __init__(self, server, starting_mid, callback):
        self._currentMID = starting_mid
        self._server = server
        self._callback = callback
        self.stopped = threading.Event()

        self._messageLayer = MessageLayer(self._currentMID)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()
        self._requestLayer = RequestLayer(self)

        try:
            # legal
            socket.inet_aton(server[0])
        except socket.error:
            # Not legal
            data = socket.getaddrinfo(server[0], server[1])
            self._server = (data[0], data[1])

        self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self._receiver_thread = threading.Thread(target=self.receive_datagram)
        self._receiver_thread.start()

    @property
    def current_mid(self):
        return self._currentMID

    @current_mid.setter
    def current_mid(self, c):
        assert isinstance(c, int)
        self._currentMID = c

    def send_message(self, message):

        if isinstance(message, Request):
            request = self._requestLayer.send_request(message)
            request = self._observeLayer.send_request(request)
            request = self._blockLayer.send_request(request)
            transaction = self._messageLayer.send_request(request)
            self.send_datagram(transaction.request)
        elif isinstance(message, Message):
            message = self._observeLayer.send_empty(message)
            message = self._blockLayer.send_empty(message)
            message = self._messageLayer.send_empty(None, None, message)
            self.send_datagram(message)

    def send_datagram(self, message):
        host, port = message.destination
        logger.debug("send_datagram - " + str(message))
        serializer = Serializer()
        message = serializer.serialize(message)

        self._socket.sendto(message, (host, port))

    def receive_datagram(self):
        logger.debug("Start receiver Thread")
        while not self.stopped.isSet():
            self._socket.settimeout(1)
            try:
                datagram, addr = self._socket.recvfrom(1152)
            except socket.timeout, e:
                err = e.args[0]
                # this next if/else is a bit redundant, but illustrates how the
                # timeout exception is setup
                if err == 'timed out':
                    continue
                else:
                    print e
                    return
            except socket.error, e:
                # Something else happened, handle error, exit, etc.
                print e
                return
            else:
    def __init__(self, server_address, multicast=False, starting_mid=None):
        """
        Initialize the server.

        :param server_address: Server address for incoming connections
        :param multicast: if the ip is a multicast address
        :param starting_mid: used for testing purposes
        """
        self.stopped = threading.Event()
        self.stopped.clear()
        self.to_be_stopped = []
        self.purge = threading.Thread(target=self.purge)
        self.purge.start()

        self._messageLayer = MessageLayer(starting_mid)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()
        self._requestLayer = RequestLayer(self)
        self.resourceLayer = ResourceLayer(self)

        # Resource directory
        root = Resource('root',
                        self,
                        visible=False,
                        observable=False,
                        allow_children=True)
        root.path = '/'
        self.root = Tree()
        self.root["/"] = root
        self._serializer = None

        self.server_address = server_address
        self.multicast = multicast
        addrinfo = socket.getaddrinfo(self.server_address[0], None)[0]

        if self.multicast:  # pragma: no cover

            # Create a socket
            self._socket = socket.socket(addrinfo[1], socket.SOCK_DGRAM)

            # Allow multiple copies of this program on one machine
            # (not strictly needed)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

            # Bind it to the port
            self._socket.bind(('', self.server_address[1]))

            group_bin = socket.inet_pton(addrinfo[1], addrinfo[4][0])
            # Join group
            if addrinfo[0] == socket.AF_INET:  # IPv4
                mreq = group_bin + struct.pack('=I', socket.INADDR_ANY)
                self._socket.setsockopt(socket.IPPROTO_IP,
                                        socket.IP_ADD_MEMBERSHIP, mreq)
            else:
                mreq = group_bin + struct.pack('@I', 0)
                self._socket.setsockopt(socket.IPPROTO_IPV6,
                                        socket.IPV6_JOIN_GROUP, mreq)

        else:
            if addrinfo[0] == socket.AF_INET:  # IPv4
                self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                        1)
            else:
                self._socket = socket.socket(socket.AF_INET6,
                                             socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                        1)

            self._socket.bind(self.server_address)
Пример #17
0
    def __init__(self,
                 server_address,
                 xml_file,
                 multicast=False,
                 starting_mid=None,
                 cache=False,
                 sock=None):
        """
        Initialize the Reverse Proxy.

        :param server_address: Server address for incoming connections
        :param xml_file: the xml file that describe remote servers
        :param multicast: if the ip is a multicast address
        :param starting_mid: used for testing purposes
        :param cache: if a cache must be used
        :param sock: if a socket has been created externally, it can be used directly
        """
        self.stopped = threading.Event()
        self.stopped.clear()
        self.to_be_stopped = []
        self.purge = threading.Thread(target=self.purge)
        self.purge.start()

        self._messageLayer = MessageLayer(starting_mid)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()

        self._forwardLayer = ForwardLayer(self)
        self.resourceLayer = ResourceLayer(self)
        self.cache_enable = cache
        if self.cache_enable:
            self._cacheLayer = CacheLayer(defines.REVERSE_PROXY)
        else:
            self._cacheLayer = None

        # Resource directory
        root = Resource('root',
                        self,
                        visible=False,
                        observable=False,
                        allow_children=True)
        root.path = '/'
        self.root = Tree()
        self.root["/"] = root
        self._serializer = None

        self.server_address = server_address
        self.multicast = multicast
        self.file_xml = xml_file
        self._mapping = {}

        addrinfo = socket.getaddrinfo(self.server_address[0], None)[0]

        if sock is not None:

            # Use given socket, could be a DTLS socket
            self._socket = sock

        elif self.multicast:  # pragma: no cover

            # Create a socket
            # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255)
            # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)

            # Join group
            if addrinfo[0] == socket.AF_INET:  # IPv4
                self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,
                                             socket.IPPROTO_UDP)

                # Allow multiple copies of this program on one machine
                # (not strictly needed)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                        1)
                self._socket.bind(
                    (defines.ALL_COAP_NODES, self.server_address[1]))
                mreq = struct.pack("4sl",
                                   socket.inet_aton(defines.ALL_COAP_NODES),
                                   socket.INADDR_ANY)
                self._socket.setsockopt(socket.IPPROTO_IP,
                                        socket.IP_ADD_MEMBERSHIP, mreq)
                self._unicast_socket = socket.socket(socket.AF_INET,
                                                     socket.SOCK_DGRAM)
                self._unicast_socket.setsockopt(socket.SOL_SOCKET,
                                                socket.SO_REUSEADDR, 1)
                self._unicast_socket.bind(self.server_address)
            else:
                self._socket = socket.socket(socket.AF_INET6,
                                             socket.SOCK_DGRAM,
                                             socket.IPPROTO_UDP)

                # Allow multiple copies of this program on one machine
                # (not strictly needed)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                        1)
                self._socket.bind(
                    (defines.ALL_COAP_NODES_IPV6, self.server_address[1]))

                addrinfo_multicast = socket.getaddrinfo(
                    defines.ALL_COAP_NODES_IPV6, 5683)[0]
                group_bin = socket.inet_pton(socket.AF_INET6,
                                             addrinfo_multicast[4][0])
                mreq = group_bin + struct.pack('@I', 0)
                self._socket.setsockopt(socket.IPPROTO_IPV6,
                                        socket.IPV6_JOIN_GROUP, mreq)
                self._unicast_socket = socket.socket(socket.AF_INET6,
                                                     socket.SOCK_DGRAM)
                self._unicast_socket.setsockopt(socket.SOL_SOCKET,
                                                socket.SO_REUSEADDR, 1)
                self._unicast_socket.bind(self.server_address)
        else:
            if addrinfo[0] == socket.AF_INET:  # IPv4
                self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                        1)
            else:
                self._socket = socket.socket(socket.AF_INET6,
                                             socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                        1)

            self._socket.bind(self.server_address)

            self.parse_config()
Пример #18
0
class CoAP(object):
    """
    Implementation of the Reverse Proxy
    """
    def __init__(self,
                 server_address,
                 xml_file,
                 multicast=False,
                 starting_mid=None,
                 cache=False,
                 sock=None):
        """
        Initialize the Reverse Proxy.

        :param server_address: Server address for incoming connections
        :param xml_file: the xml file that describe remote servers
        :param multicast: if the ip is a multicast address
        :param starting_mid: used for testing purposes
        :param cache: if a cache must be used
        :param sock: if a socket has been created externally, it can be used directly
        """
        self.stopped = threading.Event()
        self.stopped.clear()
        self.to_be_stopped = []
        self.purge = threading.Thread(target=self.purge)
        self.purge.start()

        self._messageLayer = MessageLayer(starting_mid)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()

        self._forwardLayer = ForwardLayer(self)
        self.resourceLayer = ResourceLayer(self)
        self.cache_enable = cache
        if self.cache_enable:
            self._cacheLayer = CacheLayer(defines.REVERSE_PROXY)
        else:
            self._cacheLayer = None

        # Resource directory
        root = Resource('root',
                        self,
                        visible=False,
                        observable=False,
                        allow_children=True)
        root.path = '/'
        self.root = Tree()
        self.root["/"] = root
        self._serializer = None

        self.server_address = server_address
        self.multicast = multicast
        self.file_xml = xml_file
        self._mapping = {}

        addrinfo = socket.getaddrinfo(self.server_address[0], None)[0]

        if sock is not None:

            # Use given socket, could be a DTLS socket
            self._socket = sock

        elif self.multicast:  # pragma: no cover

            # Create a socket
            # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255)
            # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)

            # Join group
            if addrinfo[0] == socket.AF_INET:  # IPv4
                self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,
                                             socket.IPPROTO_UDP)

                # Allow multiple copies of this program on one machine
                # (not strictly needed)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                        1)
                self._socket.bind(
                    (defines.ALL_COAP_NODES, self.server_address[1]))
                mreq = struct.pack("4sl",
                                   socket.inet_aton(defines.ALL_COAP_NODES),
                                   socket.INADDR_ANY)
                self._socket.setsockopt(socket.IPPROTO_IP,
                                        socket.IP_ADD_MEMBERSHIP, mreq)
                self._unicast_socket = socket.socket(socket.AF_INET,
                                                     socket.SOCK_DGRAM)
                self._unicast_socket.setsockopt(socket.SOL_SOCKET,
                                                socket.SO_REUSEADDR, 1)
                self._unicast_socket.bind(self.server_address)
            else:
                self._socket = socket.socket(socket.AF_INET6,
                                             socket.SOCK_DGRAM,
                                             socket.IPPROTO_UDP)

                # Allow multiple copies of this program on one machine
                # (not strictly needed)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                        1)
                self._socket.bind(
                    (defines.ALL_COAP_NODES_IPV6, self.server_address[1]))

                addrinfo_multicast = socket.getaddrinfo(
                    defines.ALL_COAP_NODES_IPV6, 5683)[0]
                group_bin = socket.inet_pton(socket.AF_INET6,
                                             addrinfo_multicast[4][0])
                mreq = group_bin + struct.pack('@I', 0)
                self._socket.setsockopt(socket.IPPROTO_IPV6,
                                        socket.IPV6_JOIN_GROUP, mreq)
                self._unicast_socket = socket.socket(socket.AF_INET6,
                                                     socket.SOCK_DGRAM)
                self._unicast_socket.setsockopt(socket.SOL_SOCKET,
                                                socket.SO_REUSEADDR, 1)
                self._unicast_socket.bind(self.server_address)
        else:
            if addrinfo[0] == socket.AF_INET:  # IPv4
                self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                        1)
            else:
                self._socket = socket.socket(socket.AF_INET6,
                                             socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                        1)

            self._socket.bind(self.server_address)

            self.parse_config()

    def parse_config(self):
        """
        Parse the xml file with remote servers and discover resources on each found server.
        """
        tree = ElementTree.parse(self.file_xml)
        root = tree.getroot()
        for server in root.findall('server'):
            destination = server.text
            name = server.get("name")
            self.discover_remote(destination, name)

    def discover_remote(self, destination, name):
        """
        Discover resources on remote servers.

        :param destination: the remote server (ip, port)
        :type destination: tuple
        :param name: the name of the remote server
        :type name: String
        """
        assert (isinstance(destination, str))
        if destination.startswith("["):
            split = destination.split("]", 1)
            host = split[0][1:]
            port = int(split[1][1:])
        else:
            split = destination.split(":", 1)
            host = split[0]
            port = int(split[1])
        server = (host, port)
        client = HelperClient(server)
        response = client.discover()
        client.stop()
        self.discover_remote_results(response, name)

    def discover_remote_results(self, response, name):
        """
        Create a new remote server resource for each valid discover response.

        :param response: the response to the discovery request
        :param name: the server name
        """
        host, port = response.source

        if response.code == defines.Codes.CONTENT.number:
            resource = Resource('server',
                                self,
                                visible=True,
                                observable=False,
                                allow_children=True)
            self.add_resource(name, resource)
            self._mapping[name] = (host, port)
            self.parse_core_link_format(response.payload, name, (host, port))
        else:
            logger.error("Server: " + response.source + " isn't valid.")

    def parse_core_link_format(self, link_format, base_path, remote_server):
        """
        Parse discovery results.

        :param link_format: the payload of the response to the discovery request
        :param base_path: the base path used to create child resources discovered on the remote server
        :param remote_server: the (ip, port) of the remote server
        """
        while len(link_format) > 0:
            pattern = "<([^>]*)>;"
            result = re.match(pattern, link_format)
            path = result.group(1)
            path = path.split("/")
            path = path[1:][0]
            link_format = link_format[result.end(1) + 2:]
            pattern = "([^<,])*"
            result = re.match(pattern, link_format)
            attributes = result.group(0)
            dict_att = {}
            if len(attributes) > 0:
                attributes = attributes.split(";")
                for att in attributes:
                    a = att.split("=")
                    if len(a) > 1:
                        dict_att[a[0]] = a[1]
                    else:
                        dict_att[a[0]] = a[0]
                link_format = link_format[result.end(0) + 1:]
            # TODO handle observing
            resource = RemoteResource('server',
                                      remote_server,
                                      path,
                                      coap_server=self,
                                      visible=True,
                                      observable=False,
                                      allow_children=True)
            resource.attributes = dict_att
            self.add_resource(base_path + "/" + path, resource)

        logger.info(self.root.dump())

    def purge(self):
        """
        Clean old transactions
        """
        while not self.stopped.isSet():
            self.stopped.wait(timeout=defines.EXCHANGE_LIFETIME)
            self._messageLayer.purge()

    def listen(self, timeout=10):
        """
        Listen for incoming messages. Timeout is used to check if the server must be switched off.

        :param timeout: Socket Timeout in seconds
        """
        self._socket.settimeout(float(timeout))
        while not self.stopped.isSet():
            try:
                data, client_address = self._socket.recvfrom(4096)
            except socket.timeout:
                continue
            try:

                self.receive_datagram((data, client_address))
            except RuntimeError:
                print "Exception with Executor"
        self._socket.close()

    def close(self):
        """
        Stop the server.

        """
        logger.info("Stop server")
        self.stopped.set()
        for event in self.to_be_stopped:
            event.set()
        self._socket.close()

    def receive_datagram(self, args):
        """
        Handle messages coming from the udp socket.

        :param args: (data, client_address)
        """
        data, client_address = args

        serializer = Serializer()
        message = serializer.deserialize(data, client_address)
        if isinstance(message, int):
            logger.error("receive_datagram - BAD REQUEST")

            rst = Message()
            rst.destination = client_address
            rst.type = defines.Types["RST"]
            rst.code = message
            self.send_datagram(rst)
            return
        logger.debug("receive_datagram - " + str(message))
        if isinstance(message, Request):

            transaction = self._messageLayer.receive_request(message)

            if transaction.request.duplicated and transaction.completed:
                logger.debug("message duplicated,transaction completed")
                transaction = self._observeLayer.send_response(transaction)
                transaction = self._blockLayer.send_response(transaction)
                transaction = self._messageLayer.send_response(transaction)
                self.send_datagram(transaction.response)
                return
            elif transaction.request.duplicated and not transaction.completed:
                logger.debug("message duplicated,transaction NOT completed")
                self._send_ack(transaction)
                return

            transaction.separate_timer = self._start_separate_timer(
                transaction)

            transaction = self._blockLayer.receive_request(transaction)

            if transaction.block_transfer:
                self._stop_separate_timer(transaction.separate_timer)
                transaction = self._messageLayer.send_response(transaction)
                self.send_datagram(transaction.response)
                return

            transaction = self._observeLayer.receive_request(transaction)
            """
            call to the cache layer to check if there's a cached response for the request
            if not, call the forward layer
            """
            if self._cacheLayer is not None:
                transaction = self._cacheLayer.receive_request(transaction)

                if transaction.cacheHit is False:
                    print transaction.request
                    transaction = self._forwardLayer.receive_request_reverse(
                        transaction)
                    print transaction.response

                transaction = self._observeLayer.send_response(transaction)

                transaction = self._blockLayer.send_response(transaction)

                transaction = self._cacheLayer.send_response(transaction)
            else:
                transaction = self._forwardLayer.receive_request_reverse(
                    transaction)

                transaction = self._observeLayer.send_response(transaction)

                transaction = self._blockLayer.send_response(transaction)

            self._stop_separate_timer(transaction.separate_timer)

            transaction = self._messageLayer.send_response(transaction)

            if transaction.response is not None:
                if transaction.response.type == defines.Types["CON"]:
                    self._start_retrasmission(transaction,
                                              transaction.response)
                self.send_datagram(transaction.response)

        elif isinstance(message, Message):
            transaction = self._messageLayer.receive_empty(message)
            if transaction is not None:
                transaction = self._blockLayer.receive_empty(
                    message, transaction)
                self._observeLayer.receive_empty(message, transaction)

        else:  # pragma: no cover
            logger.error("Received response from %s", message.source)

    def send_datagram(self, message):
        """
        Send a message through the udp socket.

        :type message: Message
        :param message: the message to send
        """
        if not self.stopped.isSet():
            host, port = message.destination
            logger.debug("send_datagram - " + str(message))
            serializer = Serializer()
            message = serializer.serialize(message)

            self._socket.sendto(message, (host, port))

    def add_resource(self, path, resource):
        """"
        Helper function to add resources to the resource directory during server initialization.

        :param path: the path for the new created resource
        :type resource: Resource
        :param resource: the resource to be added
        """

        assert isinstance(resource, Resource)
        path = path.strip("/")
        paths = path.split("/")
        actual_path = ""
        i = 0
        for p in paths:
            i += 1
            actual_path += "/" + p
            try:
                res = self.root[actual_path]
            except KeyError:
                res = None
            if res is None:
                if len(paths) != i:
                    return False
                resource.path = actual_path
                self.root[actual_path] = resource
        return True

    def _start_retrasmission(self, transaction, message):
        """
        Start the retransmission task.

        :type transaction: Transaction
        :param transaction: the transaction that owns the message that needs retransmission
        :type message: Message
        :param message: the message that needs the retransmission task
        """
        with transaction:
            if message.type == defines.Types['CON']:
                future_time = random.uniform(
                    defines.ACK_TIMEOUT,
                    (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR))
                transaction.retransmit_thread = threading.Thread(
                    target=self._retransmit,
                    args=(transaction, message, future_time, 0))
                transaction.retransmit_stop = threading.Event()
                self.to_be_stopped.append(transaction.retransmit_stop)
                transaction.retransmit_thread.start()

    def _retransmit(self, transaction, message, future_time, retransmit_count):
        """
        Thread function to retransmit the message in the future

        :param transaction: the transaction that owns the message that needs retransmission
        :param message: the message that needs the retransmission task
        :param future_time: the amount of time to wait before a new attempt
        :param retransmit_count: the number of retransmissions
        """
        with transaction:
            while retransmit_count < defines.MAX_RETRANSMIT and (not message.acknowledged and not message.rejected) \
                    and not self.stopped.isSet():
                transaction.retransmit_stop.wait(timeout=future_time)
                if not message.acknowledged and not message.rejected and not self.stopped.isSet(
                ):
                    retransmit_count += 1
                    future_time *= 2
                    self.send_datagram(message)

            if message.acknowledged or message.rejected:
                message.timeouted = False
            else:
                logger.warning("Give up on message {message}".format(
                    message=message.line_print))
                message.timeouted = True
                if message.observe is not None:
                    self._observeLayer.remove_subscriber(message)

            try:
                self.to_be_stopped.remove(transaction.retransmit_stop)
            except ValueError:
                pass
            transaction.retransmit_stop = None
            transaction.retransmit_thread = None

    def _start_separate_timer(self, transaction):
        """
        Start a thread to handle separate mode.

        :type transaction: Transaction
        :param transaction: the transaction that is in processing
        :rtype : the Timer object
        """
        t = threading.Timer(defines.ACK_TIMEOUT, self._send_ack,
                            (transaction, ))
        t.start()
        return t

    @staticmethod
    def _stop_separate_timer(timer):
        """
        Stop the separate Thread if an answer has been already provided to the client.

        :param timer: The Timer object
        """
        timer.cancel()

    def _send_ack(self, transaction):
        """
        Sends an ACK message for the request.

        :param transaction: the transaction that owns the request
        """

        ack = Message()
        ack.type = defines.Types['ACK']

        if not transaction.request.acknowledged:
            ack = self._messageLayer.send_empty(transaction,
                                                transaction.request, ack)
            self.send_datagram(ack)
Пример #19
0
    def __init__(self, server_address, multicast=False, starting_mid=None, cache=False, sock=None):
        """
        Initialize the Forward Proxy.

        :param server_address: Server address for incoming connections
        :param multicast: if the ip is a multicast address
        :param starting_mid: used for testing purposes
        :param cache: if a cache must be used
        :param sock: if a socket has been created externally, it can be used directly
        """
        self.stopped = threading.Event()
        self.stopped.clear()
        self.to_be_stopped = []
        self.purge = threading.Thread(target=self.purge)
        self.purge.start()
        self.cache_enable = cache

        self._messageLayer = MessageLayer(starting_mid)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()

        if self.cache_enable:
            self._cacheLayer = CacheLayer(defines.FORWARD_PROXY)
        else:
            self._cacheLayer = None

        self._forwardLayer = ForwardLayer(self)
        self.resourceLayer = ResourceLayer(self)

        # Resource directory
        root = Resource('root', self, visible=False, observable=False, allow_children=True)
        root.path = '/'
        self.root = Tree()
        self.root["/"] = root
        self._serializer = None

        self.server_address = server_address
        self.multicast = multicast

        addrinfo = socket.getaddrinfo(self.server_address[0], None)[0]

        if sock is not None:

            # Use given socket, could be a DTLS socket
            self._socket = sock

        elif self.multicast:  # pragma: no cover

            # Create a socket
            # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255)
            # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)

            # Join group
            if addrinfo[0] == socket.AF_INET:  # IPv4
                self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)

                # Allow multiple copies of this program on one machine
                # (not strictly needed)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                self._socket.bind((defines.ALL_COAP_NODES, self.server_address[1]))
                mreq = struct.pack("4sl", socket.inet_aton(defines.ALL_COAP_NODES), socket.INADDR_ANY)
                self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
                self._unicast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                self._unicast_socket.bind(self.server_address)
            else:
                self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP)

                # Allow multiple copies of this program on one machine
                # (not strictly needed)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                self._socket.bind((defines.ALL_COAP_NODES_IPV6, self.server_address[1]))

                addrinfo_multicast = socket.getaddrinfo(defines.ALL_COAP_NODES_IPV6, 5683)[0]
                group_bin = socket.inet_pton(socket.AF_INET6, addrinfo_multicast[4][0])
                mreq = group_bin + struct.pack('@I', 0)
                self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
                self._unicast_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
                self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                self._unicast_socket.bind(self.server_address)
        else:
            if addrinfo[0] == socket.AF_INET: # IPv4
                self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            else:
                self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

            self._socket.bind(self.server_address)
Пример #20
0
class CoAP(object):
    def __init__(self, server, starting_mid, callback):
        self._currentMID = starting_mid
        self._server = server
        self._callback = callback
        self.stopped = threading.Event()
        self.to_be_stopped = []

        self._messageLayer = MessageLayer(self._currentMID)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()
        self._requestLayer = RequestLayer(self)

        # try:
        #     # legal
        #     socket.inet_aton(server[0])
        # except socket.error:
        #     # Not legal
        #     data = socket.getaddrinfo(server[0], server[1])
        #     self._server = (data[0], data[1])

        host, port = self._server
        addrinfo = socket.getaddrinfo(host, None)[0]

        if addrinfo[0] == socket.AF_INET:
            self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        else:
            self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self._receiver_thread = threading.Thread(target=self.receive_datagram)
        self._receiver_thread.daemon = True
        self._receiver_thread.start()

    @property
    def current_mid(self):
        return self._currentMID

    @current_mid.setter
    def current_mid(self, c):
        assert isinstance(c, int)
        self._currentMID = c

    def send_message(self, message):

        if isinstance(message, Request):
            request = self._requestLayer.send_request(message)
            request = self._observeLayer.send_request(request)
            request = self._blockLayer.send_request(request)
            transaction = self._messageLayer.send_request(request)
            if transaction.request.type == defines.Types["CON"]:
                self._start_retransmission(transaction, transaction.request)

            self.send_datagram(transaction.request)
        elif isinstance(message, Message):
            message = self._observeLayer.send_empty(message)
            message = self._messageLayer.send_empty(None, None, message)
            self.send_datagram(message)

    def send_datagram(self, message):
        host, port = message.destination
        logger.debug("send_datagram - " + str(message))
        serializer = Serializer()
        message = serializer.serialize(message)

        self._socket.sendto(message, (host, port))

    def _start_retransmission(self, transaction, message):
        """
        Start the retransmission task.

        :type transaction: Transaction
        :param transaction: the transaction that owns the message that needs retransmission
        :type message: Message
        :param message: the message that needs the retransmission task
        """
        with transaction:
            if message.type == defines.Types['CON']:
                future_time = random.uniform(
                    defines.ACK_TIMEOUT,
                    (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR))
                transaction.retransmit_thread = threading.Thread(
                    target=self._retransmit,
                    args=(transaction, message, future_time, 0))
                transaction.retransmit_stop = threading.Event()
                self.to_be_stopped.append(transaction.retransmit_stop)
                transaction.retransmit_thread.start()

    def _retransmit(self, transaction, message, future_time, retransmit_count):
        """
        Thread function to retransmit the message in the future

        :param transaction: the transaction that owns the message that needs retransmission
        :param message: the message that needs the retransmission task
        :param future_time: the amount of time to wait before a new attempt
        :param retransmit_count: the number of retransmissions
        """
        with transaction:
            while retransmit_count < defines.MAX_RETRANSMIT and (not message.acknowledged and not message.rejected) \
                    and not self.stopped.isSet():
                transaction.retransmit_stop.wait(timeout=future_time)
                if not message.acknowledged and not message.rejected and not self.stopped.isSet(
                ):
                    logger.debug("retransmit Request")
                    retransmit_count += 1
                    future_time *= 2
                    self.send_datagram(message)

            if message.acknowledged or message.rejected:
                message.timeouted = False
            else:
                logger.warning("Give up on message {message}".format(
                    message=message.line_print))
                message.timeouted = True

            try:
                self.to_be_stopped.remove(transaction.retransmit_stop)
            except ValueError:
                pass
            transaction.retransmit_stop = None
            transaction.retransmit_thread = None

    def receive_datagram(self):
        logger.debug("Start receiver Thread")
        while not self.stopped.isSet():
            self._socket.settimeout(1)
            try:
                datagram, addr = self._socket.recvfrom(1152)
            except socket.timeout:  # pragma: no cover
                continue
            except socket.error:  # pragma: no cover
                return
            else:  # pragma: no cover
                if len(datagram) == 0:
                    print 'orderly shutdown on server end'
                    return

            serializer = Serializer()

            try:
                host, port = addr
            except ValueError:
                host, port, tmp1, tmp2 = addr

            source = (host, port)

            message = serializer.deserialize(datagram, source)

            if isinstance(message, Response):
                transaction, send_ack = self._messageLayer.receive_response(
                    message)
                if transaction is None:  # pragma: no cover
                    continue
                if send_ack:
                    self._send_ack(transaction)
                self._blockLayer.receive_response(transaction)
                if transaction.block_transfer:
                    transaction = self._messageLayer.send_request(
                        transaction.request)
                    self.send_datagram(transaction.request)
                    continue
                elif transaction is None:  # pragma: no cover
                    self._send_rst(transaction)
                    return
                self._observeLayer.receive_response(transaction)
                if transaction.notification:  # pragma: no cover
                    ack = Message()
                    ack.type = defines.Types['ACK']
                    ack = self._messageLayer.send_empty(
                        transaction, transaction.response, ack)
                    self.send_datagram(ack)
                    self._callback(transaction.response)
                else:
                    self._callback(transaction.response)
            elif isinstance(message, Message):
                self._messageLayer.receive_empty(message)

    def _send_ack(self, transaction):
        # Handle separate
        """
        Sends an ACK message for the response.

        :param transaction: transaction that holds the response
        """

        ack = Message()
        ack.type = defines.Types['ACK']

        if not transaction.response.acknowledged:
            ack = self._messageLayer.send_empty(transaction,
                                                transaction.response, ack)
            self.send_datagram(ack)

    def _send_rst(self, transaction):  # pragma: no cover
        # Handle separate
        """
        Sends an RST message for the response.

        :param transaction: transaction that holds the response
        """

        rst = Message()
        rst.type = defines.Types['RST']

        if not transaction.response.acknowledged:
            rst = self._messageLayer.send_empty(transaction,
                                                transaction.response, rst)
            self.send_datagram(rst)
Пример #21
0
class CoAP(object):
    """
    Client class to perform requests to remote servers.
    """
    def __init__(self, server, starting_mid, callback, sock=None, cb_ignore_read_exception=None, cb_ignore_write_exception=None):
        """
        Initialize the client.

        :param server: Server address for incoming connections
        :param callback:the callback function to be invoked when a response is received
        :param starting_mid: used for testing purposes
        :param sock: if a socket has been created externally, it can be used directly
        :param cb_ignore_read_exception: Callback function to handle exception raised during the socket read operation
        :param cb_ignore_write_exception: Callback function to handle exception raised during the socket write operation        
        """
        self._currentMID = starting_mid
        self._server = server
        self._callback = callback
        self._cb_ignore_read_exception = cb_ignore_read_exception
        self._cb_ignore_write_exception = cb_ignore_write_exception
        self.stopped = threading.Event()
        self.to_be_stopped = []

        self._messageLayer = MessageLayer(self._currentMID)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()
        self._requestLayer = RequestLayer(self)

        addrinfo = socket.getaddrinfo(self._server[0], None)[0]

        if sock is not None:
            self._socket = sock

        elif addrinfo[0] == socket.AF_INET:
            self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        else:
            self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        self._receiver_thread = None

    def close(self):
        """
        Stop the client.

        """
        self.stopped.set()
        for event in self.to_be_stopped:
            event.set()
        if self._receiver_thread is not None:
            self._receiver_thread.join()
        try:
            # Python does not close the OS FD on socket.close()
            # Ensure OS socket is closed with shutdown to prevent FD leak
            self._socket.shutdown(socket.SHUT_RDWR)
        except socket.error:
            pass
        self._socket.close()

    @property
    def current_mid(self):
        """
        Return the current MID.

        :return: the current mid
        """
        return self._currentMID

    @current_mid.setter
    def current_mid(self, c):
        """
        Set the current MID.

        :param c: the mid to set
        """
        assert isinstance(c, int)
        self._currentMID = c

    def send_message(self, message):
        """
        Prepare a message to send on the UDP socket. Eventually set retransmissions.

        :param message: the message to send
        """
        if isinstance(message, Request):
            request = self._requestLayer.send_request(message)
            request = self._observeLayer.send_request(request)
            request = self._blockLayer.send_request(request)
            transaction = self._messageLayer.send_request(request)
            self.send_datagram(transaction.request)
            if transaction.request.type == defines.Types["CON"]:
                self._start_retransmission(transaction, transaction.request)
        elif isinstance(message, Message):
            message = self._observeLayer.send_empty(message)
            message = self._messageLayer.send_empty(None, None, message)
            self.send_datagram(message)

    def end_observation(self, token):
        """
        Remove an observation token from our records.

        :param token: the token for the observation
        """
        dummy = Message()
        dummy.token = token
        dummy.destination = self._server
        self._observeLayer.remove_subscriber(dummy)

    @staticmethod
    def _wait_for_retransmit_thread(transaction):
        """
        Only one retransmit thread at a time, wait for other to finish
        
        """
        if hasattr(transaction, 'retransmit_thread'):
            while transaction.retransmit_thread is not None:
                logger.debug("Waiting for retransmit thread to finish ...")
                time.sleep(0.01)
                continue

    def _send_block_request(self, transaction):
        """
        A former request resulted in a block wise transfer. With this method, the block wise transfer
        will be continued, including triggering of the retry mechanism.
        
        :param transaction: The former transaction including the request which should be continued.
        """
        transaction = self._messageLayer.send_request(transaction.request)
        # ... but don't forget to reset the acknowledge flag
        transaction.request.acknowledged = False
        self.send_datagram(transaction.request)
        if transaction.request.type == defines.Types["CON"]:
            self._start_retransmission(transaction, transaction.request)

    def send_datagram(self, message):
        """
        Send a message over the UDP socket.

        :param message: the message to send
        """
        host, port = message.destination
        logger.debug("send_datagram - " + str(message))
        serializer = Serializer()
        raw_message = serializer.serialize(message)

        try:
            self._socket.sendto(raw_message, (host, port))
        except Exception as e:
            if self._cb_ignore_write_exception is not None and callable(self._cb_ignore_write_exception):
                if not self._cb_ignore_write_exception(e, self):
                    raise

        if self._receiver_thread is None or not self._receiver_thread.isAlive():
            self._receiver_thread = threading.Thread(target=self.receive_datagram)
            self._receiver_thread.start()

    def _start_retransmission(self, transaction, message):
        """
        Start the retransmission task.

        :type transaction: Transaction
        :param transaction: the transaction that owns the message that needs retransmission
        :type message: Message
        :param message: the message that needs the retransmission task
        """
        with transaction:
            if message.type == defines.Types['CON']:
                future_time = random.uniform(defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR))
                transaction.retransmit_stop = threading.Event()
                self.to_be_stopped.append(transaction.retransmit_stop)
                transaction.retransmit_thread = threading.Thread(target=self._retransmit,
                                                                 name=str('%s-Retry-%d' % (threading.current_thread().name, message.mid)),
                                                                 args=(transaction, message, future_time, 0))
                transaction.retransmit_thread.start()

    def _retransmit(self, transaction, message, future_time, retransmit_count):
        """
        Thread function to retransmit the message in the future

        :param transaction: the transaction that owns the message that needs retransmission
        :param message: the message that needs the retransmission task
        :param future_time: the amount of time to wait before a new attempt
        :param retransmit_count: the number of retransmissions
        """
        with transaction:
            logger.debug("retransmit loop ... enter")
            while retransmit_count <= defines.MAX_RETRANSMIT \
                    and (not message.acknowledged and not message.rejected) \
                    and not transaction.retransmit_stop.isSet():
                transaction.retransmit_stop.wait(timeout=future_time)
                if not message.acknowledged and not message.rejected and not transaction.retransmit_stop.isSet():
                    retransmit_count += 1
                    future_time *= 2
                    if retransmit_count < defines.MAX_RETRANSMIT:
                        logger.debug("retransmit loop ... retransmit Request")
                        self.send_datagram(message)

            if message.acknowledged or message.rejected:
                message.timeouted = False
            else:
                logger.warning("Give up on message {message}".format(message=message.line_print))
                message.timeouted = True

                # Inform the user, that nothing was received
                self._callback(message)

            try:
                self.to_be_stopped.remove(transaction.retransmit_stop)
            except ValueError:
                pass
            transaction.retransmit_stop = None
            transaction.retransmit_thread = None

            logger.debug("retransmit loop ... exit")

    def receive_datagram(self):
        """
        Receive datagram from the UDP socket and invoke the callback function.
        """
        logger.debug("Start receiver Thread")
        while not self.stopped.isSet():
            self._socket.settimeout(0.1)
            try:
                datagram, addr = self._socket.recvfrom(1152)
            except socket.timeout:  # pragma: no cover
                continue
            except Exception as e:  # pragma: no cover
                if self._cb_ignore_read_exception is not None and callable(self._cb_ignore_read_exception):
                    if self._cb_ignore_read_exception(e, self):
                        continue
                return
            else:  # pragma: no cover
                if len(datagram) == 0:
                    logger.debug("Exiting receiver Thread due to orderly shutdown on server end")
                    return

            serializer = Serializer()

            try:
                host, port = addr
            except ValueError:
                host, port, tmp1, tmp2 = addr

            source = (host, port)

            message = serializer.deserialize(datagram, source)

            if isinstance(message, Response):
                logger.debug("receive_datagram - " + str(message))
                transaction, send_ack = self._messageLayer.receive_response(message)
                if transaction is None:  # pragma: no cover
                    continue
                self._wait_for_retransmit_thread(transaction)
                if send_ack:
                    self._send_ack(transaction)
                self._blockLayer.receive_response(transaction)
                if transaction.block_transfer:
                    self._send_block_request(transaction)
                    continue
                elif transaction is None:  # pragma: no cover
                    self._send_rst(transaction)
                    return
                self._observeLayer.receive_response(transaction)
                if transaction.notification:  # pragma: no cover
                    ack = Message()
                    ack.type = defines.Types['ACK']
                    ack = self._messageLayer.send_empty(transaction, transaction.response, ack)
                    self.send_datagram(ack)
                    self._callback(transaction.response)
                else:
                    self._callback(transaction.response)
            elif isinstance(message, Message):
                self._messageLayer.receive_empty(message)

        logger.debug("Exiting receiver Thread due to request")

    def _send_ack(self, transaction):
        """
        Sends an ACK message for the response.

        :param transaction: transaction that holds the response
        """

        ack = Message()
        ack.type = defines.Types['ACK']

        if not transaction.response.acknowledged:
            ack = self._messageLayer.send_empty(transaction, transaction.response, ack)
            self.send_datagram(ack)

    def _send_rst(self, transaction):  # pragma: no cover
        """
        Sends an RST message for the response.

        :param transaction: transaction that holds the response
        """

        rst = Message()
        rst.type = defines.Types['RST']

        if not transaction.response.acknowledged:
            rst = self._messageLayer.send_empty(transaction, transaction.response, rst)
            self.send_datagram(rst)
Пример #22
0
class CoAP(object):
    """
    Implementation of the CoAP server
    """
    def __init__(self, server_address, multicast=False, starting_mid=None, sock=None, cb_ignore_listen_exception=None):
        """
        Initialize the server.

        :param server_address: Server address for incoming connections
        :param multicast: if the ip is a multicast address
        :param starting_mid: used for testing purposes
        :param sock: if a socket has been created externally, it can be used directly
        :param cb_ignore_listen_exception: Callback function to handle exception raised during the socket listen operation
        """
        self.stopped = threading.Event()
        self.stopped.clear()
        self.to_be_stopped = []
        self.purge = threading.Thread(target=self.purge)
        self.purge.start()

        self._messageLayer = MessageLayer(starting_mid)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()
        self._requestLayer = RequestLayer(self)
        self.resourceLayer = ResourceLayer(self)

        # Resource directory
        root = Resource('root', self, visible=False, observable=False, allow_children=False)
        root.path = '/'
        self.root = Tree()
        self.root["/"] = root
        self._serializer = None

        self.server_address = server_address
        self.multicast = multicast
        self._cb_ignore_listen_exception = cb_ignore_listen_exception

        addrinfo = socket.getaddrinfo(self.server_address[0], None)[0]

        if sock is not None:

            # Use given socket, could be a DTLS socket
            self._socket = sock

        elif self.multicast:  # pragma: no cover

            # Create a socket
            # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255)
            # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)

            # Join group
            if addrinfo[0] == socket.AF_INET:  # IPv4
                self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)

                # Allow multiple copies of this program on one machine
                # (not strictly needed)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                self._socket.bind((defines.ALL_COAP_NODES, self.server_address[1]))
                mreq = struct.pack("4sl", socket.inet_aton(defines.ALL_COAP_NODES), socket.INADDR_ANY)
                self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
                self._unicast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                self._unicast_socket.bind(self.server_address)
            else:
                self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP)

                # Allow multiple copies of this program on one machine
                # (not strictly needed)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                self._socket.bind((defines.ALL_COAP_NODES_IPV6, self.server_address[1]))

                addrinfo_multicast = socket.getaddrinfo(defines.ALL_COAP_NODES_IPV6, 5683)[0]
                group_bin = socket.inet_pton(socket.AF_INET6, addrinfo_multicast[4][0])
                mreq = group_bin + struct.pack('@I', 0)
                self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
                self._unicast_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
                self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                self._unicast_socket.bind(self.server_address)

        else:
            if addrinfo[0] == socket.AF_INET:  # IPv4
                self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            else:
                self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

            self._socket.bind(self.server_address)

    def purge(self):
        """
        Clean old transactions

        """
        while not self.stopped.isSet():
            self.stopped.wait(timeout=defines.EXCHANGE_LIFETIME)
            self._messageLayer.purge()

    def listen(self, timeout=10):
        """
        Listen for incoming messages. Timeout is used to check if the server must be switched off.

        :param timeout: Socket Timeout in seconds
        """
        self._socket.settimeout(float(timeout))
        while not self.stopped.isSet():
            try:
                data, client_address = self._socket.recvfrom(4096)
                if len(client_address) > 2:
                    client_address = (client_address[0], client_address[1])
            except socket.timeout:
                continue
            except Exception as e:
                if self._cb_ignore_listen_exception is not None and callable(self._cb_ignore_listen_exception):
                    if self._cb_ignore_listen_exception(e, self):
                        continue
                raise
            try:
                serializer = Serializer()
                message = serializer.deserialize(data, client_address)
                if isinstance(message, int):
                    logger.error("receive_datagram - BAD REQUEST")

                    rst = Message()
                    rst.destination = client_address
                    rst.type = defines.Types["RST"]
                    rst.code = message
                    rst.mid = self._messageLayer.fetch_mid()
                    self.send_datagram(rst)
                    continue

                logger.debug("receive_datagram - " + str(message))
                if isinstance(message, Request):
                    transaction = self._messageLayer.receive_request(message)
                    if transaction.request.duplicated and transaction.completed:
                        logger.debug("message duplicated, transaction completed")
                        if transaction.response is not None:
                            self.send_datagram(transaction.response)
                        continue
                    elif transaction.request.duplicated and not transaction.completed:
                        logger.debug("message duplicated, transaction NOT completed")
                        self._send_ack(transaction)
                        continue
                    args = (transaction, )
                    t = threading.Thread(target=self.receive_request, args=args)
                    t.start()
                # self.receive_datagram(data, client_address)
                elif isinstance(message, Response):
                    logger.error("Received response from %s", message.source)

                else:  # is Message
                    transaction = self._messageLayer.receive_empty(message)
                    if transaction is not None:
                        with transaction:
                            self._blockLayer.receive_empty(message, transaction)
                            self._observeLayer.receive_empty(message, transaction)
            except Exception as e:
                logger.error("Exception received in listen")
                logger.exception(traceback.format_exc())
                continue
            except RuntimeError:
                logger.exception("Exception with Executor")
        try:
            # Python does not close the OS FD on socket.close()
            # Ensure OS socket is closed with shutdown to prevent FD leak
            self._socket.shutdown(socket.SHUT_RDWR)
        except socket.error:
            pass
        self._socket.close()

    def close(self):
        """
        Stop the server.

        """
        logger.info("Stop server")
        self.stopped.set()
        for event in self.to_be_stopped:
            event.set()

    def receive_request(self, transaction):
        """
        Handle requests coming from the udp socket.

        :param transaction: the transaction created to manage the request
        """

        with transaction:

            transaction.separate_timer = self._start_separate_timer(transaction)

            self._blockLayer.receive_request(transaction)

            if transaction.block_transfer:
                self._stop_separate_timer(transaction.separate_timer)
                self._messageLayer.send_response(transaction)
                self.send_datagram(transaction.response)
                return

            self._observeLayer.receive_request(transaction)

            self._requestLayer.receive_request(transaction)

            if transaction.resource is not None and transaction.resource.changed:
                self.notify(transaction.resource)
                transaction.resource.changed = False
            elif transaction.resource is not None and transaction.resource.deleted:
                self.notify(transaction.resource)
                transaction.resource.deleted = False

            self._observeLayer.send_response(transaction)

            self._blockLayer.send_response(transaction)

            self._stop_separate_timer(transaction.separate_timer)

            self._messageLayer.send_response(transaction)

            if transaction.response is not None:
                if transaction.response.type == defines.Types["CON"]:
                    self._start_retransmission(transaction, transaction.response)
                self.send_datagram(transaction.response)

    def send_datagram(self, message):
        """
        Send a message through the udp socket.

        :type message: Message
        :param message: the message to send
        """
        if not self.stopped.isSet():
            host, port = message.destination
            logger.debug("send_datagram - " + str(message))
            serializer = Serializer()
            try:
                message = serializer.serialize(message)
            except Exception as e:
                logger.error("Exception received in send_datagram")
                logger.exception(traceback.format_exc())

            if self.multicast:
                self._unicast_socket.sendto(message, (host, port))
            else:
                self._socket.sendto(message, (host, port))

    def add_resource(self, path, resource):
        """
        Helper function to add resources to the resource directory during server initialization.

        :param path: the path for the new created resource
        :type resource: Resource
        :param resource: the resource to be added
        """

        assert isinstance(resource, Resource)
        path = path.strip("/")
        paths = path.split("/")
        actual_path = ""
        i = 0
        for p in paths:
            i += 1
            actual_path += "/" + p
            try:
                res = self.root[actual_path]
            except KeyError:
                res = None
            if res is None:
                if len(paths) != i:
                    return False
                resource.path = actual_path
                self.root[actual_path] = resource
        return True

    def remove_resource(self, path):
        """
        Helper function to remove resources.

        :param path: the path for the unwanted resource
        :rtype : the removed object
        """

        path = path.strip("/")
        paths = path.split("/")
        actual_path = ""
        i = 0
        for p in paths:
            i += 1
            actual_path += "/" + p
        try:
            res = self.root[actual_path]
        except KeyError:
            res = None
        if res is not None:
            del(self.root[actual_path])
        return res

    def _start_retransmission(self, transaction, message):
        """
        Start the retransmission task.

        :type transaction: Transaction
        :param transaction: the transaction that owns the message that needs retransmission
        :type message: Message
        :param message: the message that needs the retransmission task
        """
        with transaction:
            if message.type == defines.Types['CON']:
                future_time = random.uniform(defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR))
                transaction.retransmit_thread = threading.Thread(target=self._retransmit,
                                                                 args=(transaction, message, future_time, 0))
                transaction.retransmit_stop = threading.Event()
                self.to_be_stopped.append(transaction.retransmit_stop)
                transaction.retransmit_thread.start()

    def _retransmit(self, transaction, message, future_time, retransmit_count):
        """
        Thread function to retransmit the message in the future

        :param transaction: the transaction that owns the message that needs retransmission
        :param message: the message that needs the retransmission task
        :param future_time: the amount of time to wait before a new attempt
        :param retransmit_count: the number of retransmissions
        """
        with transaction:
            while retransmit_count < defines.MAX_RETRANSMIT and (not message.acknowledged and not message.rejected) \
                    and not self.stopped.isSet():
                if transaction.retransmit_stop is not None:
                    transaction.retransmit_stop.wait(timeout=future_time)
                if not message.acknowledged and not message.rejected and not self.stopped.isSet():
                    retransmit_count += 1
                    future_time *= 2
                    self.send_datagram(message)

            if message.acknowledged or message.rejected:
                message.timeouted = False
            else:
                logger.warning("Give up on message {message}".format(message=message.line_print))
                message.timeouted = True
                if message.observe is not None:
                    self._observeLayer.remove_subscriber(message)

            try:
                self.to_be_stopped.remove(transaction.retransmit_stop)
            except ValueError:
                pass
            transaction.retransmit_stop = None
            transaction.retransmit_thread = None

    def _start_separate_timer(self, transaction):
        """
        Start a thread to handle separate mode.

        :type transaction: Transaction
        :param transaction: the transaction that is in processing
        :rtype : the Timer object
        """
        t = threading.Timer(defines.ACK_TIMEOUT, self._send_ack, (transaction,))
        t.start()
        return t

    @staticmethod
    def _stop_separate_timer(timer):
        """
        Stop the separate Thread if an answer has been already provided to the client.

        :param timer: The Timer object
        """
        timer.cancel()

    def _send_ack(self, transaction):
        """
        Sends an ACK message for the request.

        :param transaction: the transaction that owns the request
        """

        ack = Message()
        ack.type = defines.Types['ACK']
        # TODO handle mutex on transaction
        if not transaction.request.acknowledged and transaction.request.type == defines.Types["CON"]:
            ack = self._messageLayer.send_empty(transaction, transaction.request, ack)
            self.send_datagram(ack)

    def notify(self, resource):
        """
        Notifies the observers of a certain resource.

        :param resource: the resource
        """
        observers = self._observeLayer.notify(resource)
        logger.debug("Notify")
        for transaction in observers:
            with transaction:
                transaction.response = None
                transaction = self._requestLayer.receive_request(transaction)
                transaction = self._observeLayer.send_response(transaction)
                transaction = self._blockLayer.send_response(transaction)
                transaction = self._messageLayer.send_response(transaction)
                if transaction.response is not None:
                    if transaction.response.type == defines.Types["CON"]:
                        self._start_retransmission(transaction, transaction.response)

                    self.send_datagram(transaction.response)
Пример #23
0
class CoAP(object):
    def __init__(self, server, starting_mid, callback):
        self._currentMID = starting_mid
        self._server = server
        self._callback = callback
        self.stopped = threading.Event()

        self._messageLayer = MessageLayer(self._currentMID)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()
        self._requestLayer = RequestLayer(self)

        # try:
        #     # legal
        #     socket.inet_aton(server[0])
        # except socket.error:
        #     # Not legal
        #     data = socket.getaddrinfo(server[0], server[1])
        #     self._server = (data[0], data[1])

        host, port = self._server
        addrinfo = socket.getaddrinfo(host, None)[0]

        if addrinfo[0] == socket.AF_INET:
            self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        else:
            self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self._receiver_thread = threading.Thread(target=self.receive_datagram)
        self._receiver_thread.daemon = True
        self._receiver_thread.start()

    @property
    def current_mid(self):
        return self._currentMID

    @current_mid.setter
    def current_mid(self, c):
        assert isinstance(c, int)
        self._currentMID = c

    def send_message(self, message):

        if isinstance(message, Request):
            request = self._requestLayer.send_request(message)
            request = self._observeLayer.send_request(request)
            request = self._blockLayer.send_request(request)
            transaction = self._messageLayer.send_request(request)
            self.send_datagram(transaction.request)
        elif isinstance(message, Message):
            message = self._observeLayer.send_empty(message)
            message = self._messageLayer.send_empty(None, None, message)
            self.send_datagram(message)

    def send_datagram(self, message):
        host, port = message.destination
        logger.debug("send_datagram - " + str(message))
        serializer = Serializer()
        message = serializer.serialize(message)

        self._socket.sendto(message, (host, port))

    def receive_datagram(self):
        logger.debug("Start receiver Thread")
        while not self.stopped.isSet():
            self._socket.settimeout(1)
            try:
                datagram, addr = self._socket.recvfrom(1152)
            except socket.timeout:  # pragma: no cover
                continue
            except socket.error:  # pragma: no cover
                return
            else:  # pragma: no cover
                if len(datagram) == 0:
                    print 'orderly shutdown on server end'
                    return

            serializer = Serializer()

            try:
                host, port = addr
            except ValueError:
                host, port, tmp1, tmp2 = addr

            source = (host, port)

            message = serializer.deserialize(datagram, source)

            if isinstance(message, Response):
                transaction, send_ack = self._messageLayer.receive_response(message)
                if transaction is None:  # pragma: no cover
                    continue
                if send_ack:
                    self._send_ack(transaction)
                self._blockLayer.receive_response(transaction)
                if transaction.block_transfer:
                    transaction = self._messageLayer.send_request(transaction.request)
                    self.send_datagram(transaction.request)
                    continue
                elif transaction is None:  # pragma: no cover
                    self._send_rst(transaction)
                    return
                self._observeLayer.receive_response(transaction)
                if transaction.notification:  # pragma: no cover
                    ack = Message()
                    ack.type = defines.Types['ACK']
                    ack = self._messageLayer.send_empty(transaction, transaction.response, ack)
                    self.send_datagram(ack)
                    self._callback(transaction.response)
                else:
                    self._callback(transaction.response)
            elif isinstance(message, Message):
                self._messageLayer.receive_empty(message)

    def _send_ack(self, transaction):
        # Handle separate
        """
        Sends an ACK message for the response.

        :param transaction: transaction that holds the response
        """

        ack = Message()
        ack.type = defines.Types['ACK']

        if not transaction.response.acknowledged:
            ack = self._messageLayer.send_empty(transaction, transaction.response, ack)
            self.send_datagram(ack)

    def _send_rst(self, transaction):  # pragma: no cover
        # Handle separate
        """
        Sends an RST message for the response.

        :param transaction: transaction that holds the response
        """

        rst = Message()
        rst.type = defines.Types['RST']

        if not transaction.response.acknowledged:
            rst = self._messageLayer.send_empty(transaction, transaction.response, rst)
            self.send_datagram(rst)
Пример #24
0
class CoAP(object):
    def __init__(self, server_address, multicast=False, starting_mid=None):

        self.stopped = threading.Event()
        self.stopped.clear()
        self.to_be_stopped = []
        self.purge = threading.Thread(target=self.purge)
        self.purge.start()

        host, port = server_address
        ret = socket.getaddrinfo(host, port)
        family, socktype, proto, canonname, sockaddr = ret[0]

        self._messageLayer = MessageLayer(starting_mid)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()

        self._forwardLayer = ForwardLayer(self)
        self.resourceLayer = ResourceLayer(self)

        # Resource directory
        root = Resource('root', self, visible=False, observable=False, allow_children=True)
        root.path = '/'
        self.root = Tree()
        self.root["/"] = root
        self._serializer = None

        self.server_address = server_address
        self.multicast = multicast

        # IPv4 or IPv6
        if len(sockaddr) == 4:
            self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        else:
            self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        if self.multicast:
            # Set some options to make it multicast-friendly
            try:
                    self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
            except AttributeError:
                    pass  # Some systems don't support SO_REUSEPORT
            self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 20)
            self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)

            # Bind to the port
            self._socket.bind(self.server_address)

            # Set some more multicast options
            interface = socket.gethostbyname(socket.gethostname())
            self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(interface))
            self._socket.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(self.server_address)
                                    + socket.inet_aton(interface))
        else:
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

            self._socket.bind(self.server_address)

    def purge(self):
        while not self.stopped.isSet():
            self.stopped.wait(timeout=defines.EXCHANGE_LIFETIME)
            self._messageLayer.purge()

    def listen(self, timeout=10):
        """
        Listen for incoming messages. Timeout is used to check if the server must be switched off.

        :param timeout: Socket Timeout in seconds
        """
        self._socket.settimeout(float(timeout))
        while not self.stopped.isSet():
            try:
                data, client_address = self._socket.recvfrom(4096)
            except socket.timeout:
                continue
            try:

                self.receive_datagram((data, client_address))
            except RuntimeError:
                print "Exception with Executor"
        self._socket.close()

    def close(self):
        """
        Stop the server.

        """
        logger.info("Stop server")
        self.stopped.set()
        for event in self.to_be_stopped:
            event.set()
        self._socket.close()

    def receive_datagram(self, args):
        """
        Receive datagram from the udp socket.

        :rtype : Message
        """
        data, client_address = args

        serializer = Serializer()
        message = serializer.deserialize(data, client_address)
        if isinstance(message, int):
            logger.error("receive_datagram - BAD REQUEST")

            rst = Message()
            rst.destination = client_address
            rst.type = defines.Types["RST"]
            rst.code = message
            self.send_datagram(rst)
            return
        logger.debug("receive_datagram - " + str(message))
        if isinstance(message, Request):

            transaction = self._messageLayer.receive_request(message)

            if transaction.request.duplicated and transaction.completed:
                logger.debug("message duplicated,transaction completed")
                transaction = self._observeLayer.send_response(transaction)
                transaction = self._blockLayer.send_response(transaction)
                transaction = self._messageLayer.send_response(transaction)
                self.send_datagram(transaction.response)
                return
            elif transaction.request.duplicated and not transaction.completed:
                logger.debug("message duplicated,transaction NOT completed")
                self._send_ack(transaction)
                return

            transaction.separate_timer = self._start_separate_timer(transaction)

            transaction = self._blockLayer.receive_request(transaction)

            if transaction.block_transfer:
                self._stop_separate_timer(transaction.separate_timer)
                transaction = self._messageLayer.send_response(transaction)
                self.send_datagram(transaction.response)
                return

            transaction = self._observeLayer.receive_request(transaction)

            transaction = self._forwardLayer.receive_request(transaction)

            if transaction.resource is not None and transaction.resource.changed:
                self.notify(transaction.resource)
                transaction.resource.changed = False
            elif transaction.resource is not None and transaction.resource.deleted:
                self.notify(transaction.resource)
                transaction.resource.deleted = False

            transaction = self._observeLayer.send_response(transaction)

            transaction = self._blockLayer.send_response(transaction)

            self._stop_separate_timer(transaction.separate_timer)

            transaction = self._messageLayer.send_response(transaction)

            if transaction.response is not None:
                if transaction.response.type == defines.Types["CON"]:
                    self._start_retrasmission(transaction, transaction.response)
                self.send_datagram(transaction.response)

        elif isinstance(message, Message):
            transaction = self._messageLayer.receive_empty(message)
            if transaction is not None:
                transaction = self._blockLayer.receive_empty(message, transaction)
                self._observeLayer.receive_empty(message, transaction)

        else:  # is Response
            logger.error("Received response from %s", message.source)

    def send_datagram(self, message):
        """

        :type message: Message
        :param message:
        """
        if not self.stopped.isSet():
            host, port = message.destination
            logger.debug("send_datagram - " + str(message))
            serializer = Serializer()
            message = serializer.serialize(message)

            self._socket.sendto(message, (host, port))

    def _start_retrasmission(self, transaction, message):
        """

        :type transaction: Transaction
        :param transaction:
        :type message: Message
        :param message:
        :rtype : Future
        """
        if message.type == defines.Types['CON']:
            future_time = random.uniform(defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR))
            transaction.retransmit_thread = threading.Thread(target=self._retransmit,
                                                             args=(transaction, message, future_time, 0))
            transaction.retransmit_stop = threading.Event()
            self.to_be_stopped.append(transaction.retransmit_stop)
            transaction.retransmit_thread.start()

    def _retransmit(self, transaction, message, future_time, retransmit_count):
        while retransmit_count < defines.MAX_RETRANSMIT and (not message.acknowledged and not message.rejected) \
                and not self.stopped.isSet():
            transaction.retransmit_stop.wait(timeout=future_time)
            if not message.acknowledged and not message.rejected and not self.stopped.isSet():
                retransmit_count += 1
                future_time *= 2
                self.send_datagram(message)

        if message.acknowledged or message.rejected:
            message.timeouted = False
        else:
            logger.warning("Give up on message {message}".format(message=message.line_print))
            message.timeouted = True
            if message.observe is not None:
                self._observeLayer.remove_subscriber(message)

        try:
            self.to_be_stopped.remove(transaction.retransmit_stop)
        except ValueError:
            pass
        transaction.retransmit_stop = None
        transaction.retransmit_thread = None

    def _start_separate_timer(self, transaction):
        """

        :type transaction: Transaction
        :param transaction:
        :type message: Message
        :param message:
        :rtype : Future
        """
        t = threading.Timer(defines.ACK_TIMEOUT, self._send_ack, (transaction,))
        t.start()
        return t

    @staticmethod
    def _stop_separate_timer(timer):
        """

        :type future: Future
        :param future:
        """
        timer.cancel()

    def _send_ack(self, transaction):
        # Handle separate
        """
        Sends an ACK message for the request.

        :param request: [request, sleep_time] or request
        """

        ack = Message()
        ack.type = defines.Types['ACK']

        if not transaction.request.acknowledged:
            ack = self._messageLayer.send_empty(transaction, transaction.request, ack)
            self.send_datagram(ack)
Пример #25
0
class CoAP(object):
    def __init__(self, server_address, multicast=False, starting_mid=None):

        """
        Initialize the server.

        :param server_address: Server address for incoming connections
        :param multicast: if the ip is a multicast address
        :param starting_mid: used for testing purposes
        """
        self.stopped = threading.Event()
        self.stopped.clear()
        self.to_be_stopped = []
        self.purge = threading.Thread(target=self.purge)
        self.purge.start()

        self._messageLayer = MessageLayer(starting_mid)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()
        self._requestLayer = RequestLayer(self)
        self.resourceLayer = ResourceLayer(self)

        # Resource directory
        root = Resource('root', self, visible=False, observable=False, allow_children=False)
        root.path = '/'
        self.root = Tree()
        self.root["/"] = root
        self._serializer = None

        self.server_address = server_address
        self.multicast = multicast

        addrinfo = socket.getaddrinfo(self.server_address[0], None)[0]

        if self.multicast:  # pragma: no cover

            # Create a socket
            self._socket = socket.socket(addrinfo[1], socket.SOCK_DGRAM)

            # Allow multiple copies of this program on one machine
            # (not strictly needed)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

            # Bind it to the port
            self._socket.bind(('', self.server_address[1]))

            group_bin = socket.inet_pton(addrinfo[1], addrinfo[4][0])
            # Join group
            if addrinfo[0] == socket.AF_INET: # IPv4
                mreq = group_bin + struct.pack('=I', socket.INADDR_ANY)
                self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
            else:
                mreq = group_bin + struct.pack('@I', 0)
                self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)

        else:
            if addrinfo[0] == socket.AF_INET:  # IPv4
                self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                self._socket.bind(self.server_address)
            else:
                
                self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
                self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                print "Yes Socket IPV6 is "
                print self.server_address
                self._socket.bind(self.server_address)

            

    def purge(self):
        """
        Clean old transactions

        """
        while not self.stopped.isSet():
            self.stopped.wait(timeout=defines.EXCHANGE_LIFETIME)
            self._messageLayer.purge()

    def listen(self, timeout=10):
        """
        Listen for incoming messages. Timeout is used to check if the server must be switched off.

        :param timeout: Socket Timeout in seconds
        """
        self._socket.settimeout(float(timeout))
        while not self.stopped.isSet():
            try:
                data, client_address = self._socket.recvfrom(4096)
                if len(client_address) > 2:
                    client_address = (client_address[0], client_address[1])
            except socket.timeout:
                continue
            try:
                serializer = Serializer()
                message = serializer.deserialize(data, client_address)
                if isinstance(message, int):
                    logger.error("receive_datagram - BAD REQUEST")

                    rst = Message()
                    rst.destination = client_address
                    rst.type = defines.Types["RST"]
                    rst.code = message
                    rst.mid = self._messageLayer._current_mid
                    self._messageLayer._current_mid += 1 % 65535
                    self.send_datagram(rst)
                    continue

                logger.debug("receive_datagram - " + str(message))
                if isinstance(message, Request):
                    transaction = self._messageLayer.receive_request(message)
                    if transaction.request.duplicated and transaction.completed:
                        logger.debug("message duplicated, transaction completed")
                        if transaction.response is not None:
                            self.send_datagram(transaction.response)
                        return
                    elif transaction.request.duplicated and not transaction.completed:
                        logger.debug("message duplicated, transaction NOT completed")
                        self._send_ack(transaction)
                        return
                    args = (transaction, )
                    t = threading.Thread(target=self.receive_request, args=args)
                    t.start()
                # self.receive_datagram(data, client_address)
                elif isinstance(message, Response):
                    logger.error("Received response from %s", message.source)

                else:  # is Message
                    transaction = self._messageLayer.receive_empty(message)
                    if transaction is not None:
                        with transaction:
                            self._blockLayer.receive_empty(message, transaction)
                            self._observeLayer.receive_empty(message, transaction)

            except RuntimeError:
                print "Exception with Executor"
        self._socket.close()

    def close(self):
        """
        Stop the server.

        """
        logger.info("Stop server")
        self.stopped.set()
        for event in self.to_be_stopped:
            event.set()
        self._socket.close()

    def receive_request(self, transaction):
        """
        Receive datagram from the udp socket.

        :param data: the udp message
        :param client_address: the ip and port of the client
        """

        with transaction:

            transaction.separate_timer = self._start_separate_timer(transaction)

            self._blockLayer.receive_request(transaction)

            if transaction.block_transfer:
                self._stop_separate_timer(transaction.separate_timer)
                self._messageLayer.send_response(transaction)
                self.send_datagram(transaction.response)
                return

            self._observeLayer.receive_request(transaction)

            self._requestLayer.receive_request(transaction)

            if transaction.resource is not None and transaction.resource.changed:
                self.notify(transaction.resource)
                transaction.resource.changed = False
            elif transaction.resource is not None and transaction.resource.deleted:
                self.notify(transaction.resource)
                transaction.resource.deleted = False

            self._observeLayer.send_response(transaction)

            self._blockLayer.send_response(transaction)

            self._stop_separate_timer(transaction.separate_timer)

            self._messageLayer.send_response(transaction)

            if transaction.response is not None:
                if transaction.response.type == defines.Types["CON"]:
                    self._start_retransmission(transaction, transaction.response)
                self.send_datagram(transaction.response)

    def send_datagram(self, message):
        """

        :type message: Message
        :param message:
        """
        if not self.stopped.isSet():
            host, port = message.destination
            logger.debug("send_datagram - " + str(message))
            serializer = Serializer()
            message = serializer.serialize(message)

            self._socket.sendto(message, (host, port))

    def add_resource(self, path, resource):
        """
        Helper function to add resources to the resource directory during server initialization.

        :param path: the path for the new created resource
        :type resource: Resource
        :param resource: the resource to be added
        """

        assert isinstance(resource, Resource)
        path = path.strip("/")
        paths = path.split("/")
        actual_path = ""
        i = 0
        for p in paths:
            i += 1
            actual_path += "/" + p
            try:
                res = self.root[actual_path]
            except KeyError:
                res = None
            if res is None:
                if len(paths) != i:
                    return False
                resource.path = actual_path
                self.root[actual_path] = resource
        return True

    def _start_retransmission(self, transaction, message):
        """
        Start the retransmission task.

        :type transaction: Transaction
        :param transaction: the transaction that owns the message that needs retransmission
        :type message: Message
        :param message: the message that needs the retransmission task
        """
        with transaction:
            if message.type == defines.Types['CON']:
                future_time = random.uniform(defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR))
                transaction.retransmit_thread = threading.Thread(target=self._retransmit,
                                                                 args=(transaction, message, future_time, 0))
                transaction.retransmit_stop = threading.Event()
                self.to_be_stopped.append(transaction.retransmit_stop)
                transaction.retransmit_thread.start()

    def _retransmit(self, transaction, message, future_time, retransmit_count):
        """
        Thread function to retransmit the message in the future

        :param transaction: the transaction that owns the message that needs retransmission
        :param message: the message that needs the retransmission task
        :param future_time: the amount of time to wait before a new attempt
        :param retransmit_count: the number of retransmissions
        """
        with transaction:
            while retransmit_count < defines.MAX_RETRANSMIT and (not message.acknowledged and not message.rejected) \
                    and not self.stopped.isSet():
                transaction.retransmit_stop.wait(timeout=future_time)
                if not message.acknowledged and not message.rejected and not self.stopped.isSet():
                    retransmit_count += 1
                    future_time *= 2
                    self.send_datagram(message)

            if message.acknowledged or message.rejected:
                message.timeouted = False
            else:
                logger.warning("Give up on message {message}".format(message=message.line_print))
                message.timeouted = True
                if message.observe is not None:
                    self._observeLayer.remove_subscriber(message)

            try:
                self.to_be_stopped.remove(transaction.retransmit_stop)
            except ValueError:
                pass
            transaction.retransmit_stop = None
            transaction.retransmit_thread = None

    def _start_separate_timer(self, transaction):
        """
        Start a thread to handle separate mode.

        :type transaction: Transaction
        :param transaction: the transaction that is in processing
        :rtype : the Timer object
        """
        t = threading.Timer(defines.ACK_TIMEOUT, self._send_ack, (transaction,))
        t.start()
        return t

    @staticmethod
    def _stop_separate_timer(timer):
        """
        Stop the separate Thread if an answer has been already provided to the client.

        :param timer: The Timer object
        """
        timer.cancel()

    def _send_ack(self, transaction):
        """
        Sends an ACK message for the request.

        :param transaction: the transaction that owns the request
        """

        ack = Message()
        ack.type = defines.Types['ACK']
        # TODO handle mutex on transaction
        if not transaction.request.acknowledged and transaction.request.type == defines.Types["CON"]:
            ack = self._messageLayer.send_empty(transaction, transaction.request, ack)
            self.send_datagram(ack)

    def notify(self, resource):
        """
        Notifies the observers of a certain resource.

        :param resource: the resource
        """
        observers = self._observeLayer.notify(resource)
        logger.debug("Notify")
        for transaction in observers:
            with transaction:
                transaction.response = None
                transaction = self._requestLayer.receive_request(transaction)
                transaction = self._observeLayer.send_response(transaction)
                transaction = self._blockLayer.send_response(transaction)
                transaction = self._messageLayer.send_response(transaction)
                if transaction.response is not None:
                    if transaction.response.type == defines.Types["CON"]:
                        self._start_retransmission(transaction, transaction.response)

                    self.send_datagram(transaction.response)
Пример #26
0
class CoAP(object):
    """
    Client class to perform requests to remote servers.
    """
    def __init__(self,
                 server,
                 starting_mid,
                 callback,
                 sock=None,
                 cb_ignore_read_exception=None,
                 cb_ignore_write_exception=None):
        """
        Initialize the client.

        :param server: Server address for incoming connections
        :param callback:the callback function to be invoked when a response is received
        :param starting_mid: used for testing purposes
        :param sock: if a socket has been created externally, it can be used directly
        :param cb_ignore_read_exception: Callback function to handle exception raised during the socket read operation
        :param cb_ignore_write_exception: Callback function to handle exception raised during the socket write operation        
        """
        self._currentMID = starting_mid
        self._server = server
        self._callback = callback
        self._cb_ignore_read_exception = cb_ignore_read_exception
        self._cb_ignore_write_exception = cb_ignore_write_exception
        self.stopped = threading.Event()
        self.to_be_stopped = []

        self._messageLayer = MessageLayer(self._currentMID)
        self._blockLayer = BlockLayer()
        self._observeLayer = ObserveLayer()
        self._requestLayer = RequestLayer(self)

        addrinfo = socket.getaddrinfo(self._server[0], None)[0]

        if sock is not None:
            self._socket = sock

        elif addrinfo[0] == socket.AF_INET:
            self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        else:
            self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        self._receiver_thread = None

    def close(self):
        """
        Stop the client.

        """
        self.stopped.set()
        for event in self.to_be_stopped:
            event.set()
        if self._receiver_thread is not None:
            self._receiver_thread.join()
        self._socket.close()

    @property
    def current_mid(self):
        """
        Return the current MID.

        :return: the current mid
        """
        return self._currentMID

    @current_mid.setter
    def current_mid(self, c):
        """
        Set the current MID.

        :param c: the mid to set
        """
        assert isinstance(c, int)
        self._currentMID = c

    def send_message(self, message):
        """
        Prepare a message to send on the UDP socket. Eventually set retransmissions.

        :param message: the message to send
        """
        if isinstance(message, Request):
            request = self._requestLayer.send_request(message)
            request = self._observeLayer.send_request(request)
            request = self._blockLayer.send_request(request)
            transaction = self._messageLayer.send_request(request)
            self.send_datagram(transaction.request)
            if transaction.request.type == defines.Types["CON"]:
                self._start_retransmission(transaction, transaction.request)
        elif isinstance(message, Message):
            message = self._observeLayer.send_empty(message)
            message = self._messageLayer.send_empty(None, None, message)
            self.send_datagram(message)

    @staticmethod
    def _wait_for_retransmit_thread(transaction):
        """
        Only one retransmit thread at a time, wait for other to finish
        
        """
        if hasattr(transaction, 'retransmit_thread'):
            while transaction.retransmit_thread is not None:
                logger.debug("Waiting for retransmit thread to finish ...")
                time.sleep(0.01)
                continue

    def _send_block_request(self, transaction):
        """
        A former request resulted in a block wise transfer. With this method, the block wise transfer
        will be continued, including triggering of the retry mechanism.
        
        :param transaction: The former transaction including the request which should be continued.
        """
        transaction = self._messageLayer.send_request(transaction.request)
        # ... but don't forget to reset the acknowledge flag
        transaction.request.acknowledged = False
        self.send_datagram(transaction.request)
        if transaction.request.type == defines.Types["CON"]:
            self._start_retransmission(transaction, transaction.request)

    def send_datagram(self, message):
        """
        Send a message over the UDP socket.

        :param message: the message to send
        """
        host, port = message.destination
        logger.debug("send_datagram - " + str(message))
        serializer = Serializer()
        raw_message = serializer.serialize(message)

        try:
            self._socket.sendto(raw_message, (host, port))
        except Exception as e:
            if self._cb_ignore_write_exception is not None and callable(
                    self._cb_ignore_write_exception):
                if not self._cb_ignore_write_exception(e, self):
                    raise

        if self._receiver_thread is None or not self._receiver_thread.isAlive(
        ):
            self._receiver_thread = threading.Thread(
                target=self.receive_datagram)
            self._receiver_thread.start()

    def _start_retransmission(self, transaction, message):
        """
        Start the retransmission task.

        :type transaction: Transaction
        :param transaction: the transaction that owns the message that needs retransmission
        :type message: Message
        :param message: the message that needs the retransmission task
        """
        with transaction:
            if message.type == defines.Types['CON']:
                future_time = random.uniform(
                    defines.ACK_TIMEOUT,
                    (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR))
                transaction.retransmit_stop = threading.Event()
                self.to_be_stopped.append(transaction.retransmit_stop)
                transaction.retransmit_thread = threading.Thread(
                    target=self._retransmit,
                    name=str('%s-Retry-%d' %
                             (threading.current_thread().name, message.mid)),
                    args=(transaction, message, future_time, 0))
                transaction.retransmit_thread.start()

    def _retransmit(self, transaction, message, future_time, retransmit_count):
        """
        Thread function to retransmit the message in the future

        :param transaction: the transaction that owns the message that needs retransmission
        :param message: the message that needs the retransmission task
        :param future_time: the amount of time to wait before a new attempt
        :param retransmit_count: the number of retransmissions
        """
        with transaction:
            logger.debug("retransmit loop ... enter")
            while retransmit_count <= defines.MAX_RETRANSMIT \
                    and (not message.acknowledged and not message.rejected) \
                    and not transaction.retransmit_stop.isSet():
                transaction.retransmit_stop.wait(timeout=future_time)
                if not message.acknowledged and not message.rejected and not transaction.retransmit_stop.isSet(
                ):
                    retransmit_count += 1
                    future_time *= 2
                    if retransmit_count < defines.MAX_RETRANSMIT:
                        logger.debug("retransmit loop ... retransmit Request")
                        self.send_datagram(message)

            if message.acknowledged or message.rejected:
                message.timeouted = False
            else:
                logger.warning("Give up on message {message}".format(
                    message=message.line_print))
                message.timeouted = True

                # Inform the user, that nothing was received
                self._callback(None)

            try:
                self.to_be_stopped.remove(transaction.retransmit_stop)
            except ValueError:
                pass
            transaction.retransmit_stop = None
            transaction.retransmit_thread = None

            logger.debug("retransmit loop ... exit")

    def receive_datagram(self):
        """
        Receive datagram from the UDP socket and invoke the callback function.
        """
        logger.debug("Start receiver Thread")
        while not self.stopped.isSet():
            self._socket.settimeout(0.1)
            try:
                datagram, addr = self._socket.recvfrom(1152)
            except socket.timeout:  # pragma: no cover
                continue
            except Exception as e:  # pragma: no cover
                if self._cb_ignore_read_exception is not None and callable(
                        self._cb_ignore_read_exception):
                    if self._cb_ignore_read_exception(e, self):
                        continue
                return
            else:  # pragma: no cover
                if len(datagram) == 0:
                    logger.debug(
                        "Exiting receiver Thread due to orderly shutdown on server end"
                    )
                    return

            serializer = Serializer()

            try:
                host, port = addr
            except ValueError:
                host, port, tmp1, tmp2 = addr

            source = (host, port)

            message = serializer.deserialize(datagram, source)

            if isinstance(message, Response):
                logger.debug("receive_datagram - " + str(message))
                transaction, send_ack = self._messageLayer.receive_response(
                    message)
                if transaction is None:  # pragma: no cover
                    continue
                self._wait_for_retransmit_thread(transaction)
                if send_ack:
                    self._send_ack(transaction)
                self._blockLayer.receive_response(transaction)
                if transaction.block_transfer:
                    self._send_block_request(transaction)
                    continue
                elif transaction is None:  # pragma: no cover
                    self._send_rst(transaction)
                    return
                self._observeLayer.receive_response(transaction)
                if transaction.notification:  # pragma: no cover
                    ack = Message()
                    ack.type = defines.Types['ACK']
                    ack = self._messageLayer.send_empty(
                        transaction, transaction.response, ack)
                    self.send_datagram(ack)
                    self._callback(transaction.response)
                else:
                    self._callback(transaction.response)
            elif isinstance(message, Message):
                self._messageLayer.receive_empty(message)

        logger.debug("Exiting receiver Thread due to request")

    def _send_ack(self, transaction):
        """
        Sends an ACK message for the response.

        :param transaction: transaction that holds the response
        """

        ack = Message()
        ack.type = defines.Types['ACK']

        if not transaction.response.acknowledged:
            ack = self._messageLayer.send_empty(transaction,
                                                transaction.response, ack)
            self.send_datagram(ack)

    def _send_rst(self, transaction):  # pragma: no cover
        """
        Sends an RST message for the response.

        :param transaction: transaction that holds the response
        """

        rst = Message()
        rst.type = defines.Types['RST']

        if not transaction.response.acknowledged:
            rst = self._messageLayer.send_empty(transaction,
                                                transaction.response, rst)
            self.send_datagram(rst)