Example #1
0
        async def test_coro():
            async with create_broker(
                test_config, plugin_namespace="distmqtt.test.plugins"
            ) as broker:
                broker.plugins_manager._tg = broker._tg
                self.assertTrue(broker.transitions.is_started())

                async with await anyio.connect_tcp("127.0.0.1", PORT) as conn:
                    stream = StreamAdapter(conn)

                    vh = ConnectVariableHeader()
                    payload = ConnectPayload()

                    vh.keep_alive = 10
                    vh.clean_session_flag = False
                    vh.will_retain_flag = False
                    payload.client_id = "test_id"
                    connect = ConnectPacket(vh=vh, payload=payload)
                    await connect.to_stream(stream)
                    await ConnackPacket.from_stream(stream)

                    publish_1 = PublishPacket.build("/test", b"data", 1, False, QOS_2, False)
                    await publish_1.to_stream(stream)
                    await PubrecPacket.from_stream(stream)

                    publish_dup = PublishPacket.build("/test", b"data", 1, True, QOS_2, False)
                    await publish_dup.to_stream(stream)
                    await PubrecPacket.from_stream(stream)
                    pubrel = PubrelPacket.build(1)
                    await pubrel.to_stream(stream)
                    # await PubcompPacket.from_stream(stream)

                    disconnect = DisconnectPacket()
                    await disconnect.to_stream(stream)
Example #2
0
        async def test_coro():
            async with create_broker(
                    test_config,
                    plugin_namespace="distmqtt.test.plugins") as broker:
                broker.plugins_manager._tg = broker._tg
                self.assertTrue(broker.transitions.is_started())

                async with await anyio.connect_tcp("127.0.0.1", 1883) as conn:
                    stream = StreamAdapter(conn)

                    vh = ConnectVariableHeader()
                    payload = ConnectPayload()

                    vh.keep_alive = 10
                    vh.clean_session_flag = False
                    vh.will_retain_flag = False
                    vh.will_flag = True
                    vh.will_qos = QOS_0
                    payload.client_id = 'test_id'
                    payload.will_message = b'test'
                    payload.will_topic = '/topic'
                    connect = ConnectPacket(vh=vh, payload=payload)
                    await connect.to_stream(stream)
                    await ConnackPacket.from_stream(stream)

                    disconnect = DisconnectPacket()
                    await disconnect.to_stream(stream)

            self.assertTrue(broker.transitions.is_stopped())
            self.assertDictEqual(broker._sessions, {})
Example #3
0
 async def stream_connected(self, conn, listener_name):
     await self.client_connected(listener_name, StreamAdapter(conn))
Example #4
0
    async def _connect_coro(self):
        kwargs = dict()

        # Decode URI attributes
        uri_attributes = urlparse(self.session.broker_uri)
        scheme = uri_attributes.scheme
        secure = True if scheme in ("mqtts", "wss") else False
        self.session.username = (self.session.username if self.session.username
                                 else uri_attributes.username)
        self.session.password = (self.session.password if self.session.password
                                 else uri_attributes.password)
        self.session.remote_address = uri_attributes.hostname
        self.session.remote_port = uri_attributes.port
        if scheme in ("mqtt", "mqtts") and not self.session.remote_port:
            self.session.remote_port = 8883 if scheme == "mqtts" else 1883
        if scheme in ("ws", "wss") and not self.session.remote_port:
            self.session.remote_port = 443 if scheme == "wss" else 80
        if scheme in ("ws", "wss"):
            # Rewrite URI to conform to https://tools.ietf.org/html/rfc6455#section-3
            uri = (
                scheme,
                self.session.remote_address + ":" +
                str(self.session.remote_port),
                uri_attributes[2],
                uri_attributes[3],
                uri_attributes[4],
                uri_attributes[5],
            )
            self.session.broker_uri = urlunparse(uri)
        # Init protocol handler
        # if not self._handler:
        self._handler = ClientProtocolHandler(self.plugins_manager)

        if secure:
            sc = ssl.create_default_context(
                ssl.Purpose.SERVER_AUTH,
                cafile=self.session.cafile,
                capath=self.session.capath,
                cadata=self.session.cadata,
            )
            if "certfile" in self.config and "keyfile" in self.config:
                sc.load_cert_chain(self.config["certfile"],
                                   self.config["keyfile"])
            if "check_hostname" in self.config and isinstance(
                    self.config["check_hostname"], bool):
                sc.check_hostname = self.config["check_hostname"]
            kwargs["ssl_context"] = sc
            kwargs["autostart_tls"] = True

        try:
            adapter = None
            self._connected_state.clear()
            # Open connection
            if scheme in ("mqtt", "mqtts"):
                conn = await anyio.connect_tcp(self.session.remote_address,
                                               self.session.remote_port,
                                               **kwargs)
                if secure:
                    await conn.start_tls()
                adapter = StreamAdapter(conn)
            elif scheme in ("ws", "wss"):
                if kwargs.pop("autostart_tls", False):
                    kwargs["ssl"] = kwargs.pop("ssl_context")
                websocket = await create_websocket(self.session.broker_uri,
                                                   subprotocols=["mqtt"],
                                                   headers=self.extra_headers,
                                                   **kwargs)
                adapter = WebSocketsAdapter(websocket)
            # Start MQTT protocol
            await self._handler.attach(self.session, adapter)
            try:
                return_code = await self._handler.mqtt_connect()
            except NoDataException:
                self.logger.warning("Connection broken by broker")
                raise ConnectException("Connection broken by broker")
            if return_code != CONNECTION_ACCEPTED:
                self.session.transitions.disconnect()
                self.logger.warning("Connection rejected with code '%s'",
                                    return_code)
                raise ConnectException("Connection rejected by broker",
                                       return_code)
            # Handle MQTT protocol
            await self._handler.start()
            self.session.transitions.connect()
            await self._connected_state.set()
            topics = []
            if self._subscriptions is not None:
                for s in self._subscriptions.values():
                    topics.append(("/".join(s.topic), s.qos))
                if topics:
                    await self.subscribe(topics)
            self.logger.debug(
                "connected to %s:%s",
                self.session.remote_address,
                self.session.remote_port,
            )
            return return_code
        except ProtocolError as exc:
            self.logger.warning(
                "connection failed: invalid websocket handshake")
            self.session.transitions.disconnect()
            raise ConnectException(
                "connection failed: invalid websocket handshake") from exc
        except (ProtocolHandlerException, ConnectionError, OSError) as exc:
            self.logger.warning("MQTT connection failed")
            self.session.transitions.disconnect()
            raise ConnectException from exc
Example #5
0
    async def _connect_coro(self):
        kwargs = dict()

        # Decode URI attributes
        uri_attributes = urlparse(self.session.broker_uri)
        scheme = uri_attributes.scheme
        secure = True if scheme in ('mqtts', 'wss') else False
        self.session.username = self.session.username if self.session.username else uri_attributes.username
        self.session.password = self.session.password if self.session.password else uri_attributes.password
        self.session.remote_address = uri_attributes.hostname
        self.session.remote_port = uri_attributes.port
        if scheme in ('mqtt', 'mqtts') and not self.session.remote_port:
            self.session.remote_port = 8883 if scheme == 'mqtts' else 1883
        if scheme in ('ws', 'wss') and not self.session.remote_port:
            self.session.remote_port = 443 if scheme == 'wss' else 80
        if scheme in ('ws', 'wss'):
            # Rewrite URI to conform to https://tools.ietf.org/html/rfc6455#section-3
            uri = (scheme, self.session.remote_address + ":" +
                   str(self.session.remote_port), uri_attributes[2],
                   uri_attributes[3], uri_attributes[4], uri_attributes[5])
            self.session.broker_uri = urlunparse(uri)
        # Init protocol handler
        #if not self._handler:
        self._handler = ClientProtocolHandler(self.plugins_manager)

        if secure:
            sc = ssl.create_default_context(ssl.Purpose.SERVER_AUTH,
                                            cafile=self.session.cafile,
                                            capath=self.session.capath,
                                            cadata=self.session.cadata)
            if 'certfile' in self.config and 'keyfile' in self.config:
                sc.load_cert_chain(self.config['certfile'],
                                   self.config['keyfile'])
            if 'check_hostname' in self.config and isinstance(
                    self.config['check_hostname'], bool):
                sc.check_hostname = self.config['check_hostname']
            kwargs['ssl_context'] = sc
            kwargs['autostart_tls'] = True

        try:
            adapter = None
            self._connected_state.clear()
            # Open connection
            if scheme in ('mqtt', 'mqtts'):
                conn = \
                    await anyio.connect_tcp(
                        self.session.remote_address,
                        self.session.remote_port, **kwargs)
                if secure:
                    await conn.start_tls()
                adapter = StreamAdapter(conn)
            elif scheme in ('ws', 'wss'):
                if kwargs.pop('autostart_tls', False):
                    kwargs['ssl'] = kwargs.pop('ssl_context')
                websocket = await create_websocket(self.session.broker_uri,
                                                   subprotocols=['mqtt'],
                                                   headers=self.extra_headers,
                                                   **kwargs)
                adapter = WebSocketsAdapter(websocket)
            # Start MQTT protocol
            await self._handler.attach(self.session, adapter)
            try:
                return_code = await self._handler.mqtt_connect()
            except NoDataException:
                self.logger.warning("Connection broken by broker")
                exc = ConnectException("Connection broken by broker")
                raise exc
            if return_code is not CONNECTION_ACCEPTED:
                self.session.transitions.disconnect()
                self.logger.warning("Connection rejected with code '%s'",
                                    return_code)
                exc = ConnectException("Connection rejected by broker")
                exc.return_code = return_code
                raise exc
            else:
                # Handle MQTT protocol
                await self._handler.start()
                self.session.transitions.connect()
                await self._connected_state.set()
                self.logger.debug("connected to %s:%s",
                                  self.session.remote_address,
                                  self.session.remote_port)
            return return_code
        except ProtocolError as exc:
            self.logger.warning(
                "connection failed: invalid websocket handshake")
            self.session.transitions.disconnect()
            raise ConnectException(
                "connection failed: invalid websocket handshake") from exc
        except (ProtocolHandlerException, ConnectionError, OSError) as exc:
            self.logger.warning("MQTT connection failed")
            self.session.transitions.disconnect()
            raise ConnectException from exc
Example #6
0
    async def init_from_connect(cls, stream: StreamAdapter, plugins_manager):
        """

        :param stream:
        :param plugins_manager:
        :return:
        """
        remote_address, remote_port = stream.get_peer_info()
        try:
            connect = await ConnectPacket.from_stream(stream)
        except NoDataException:
            raise MQTTException("Client closed the connection")
        logger.debug("< B %r", connect)
        await plugins_manager.fire_event(EVENT_MQTT_PACKET_RECEIVED,
                                         packet=connect)
        # this shouldn't be required anymore since broker generates for each client a random client_id if not provided
        # [MQTT-3.1.3-6]
        if connect.payload.client_id is None:
            raise MQTTException(
                "[[MQTT-3.1.3-3]] : Client identifier must be present")

        if connect.variable_header.will_flag:
            if connect.payload.will_topic is None or connect.payload.will_message is None:
                raise MQTTException(
                    "will flag set, but will topic/message not present in payload"
                )

        if connect.variable_header.reserved_flag:
            raise MQTTException(
                "[MQTT-3.1.2-3] CONNECT reserved flag must be set to 0")
        if connect.proto_name != "MQTT":
            raise MQTTException(
                '[MQTT-3.1.2-1] Incorrect protocol name: "%s"' %
                connect.proto_name)

        connack = None
        error_msg = None
        if connect.proto_level != 4:
            # only MQTT 3.1.1 supported
            error_msg = "Invalid protocol from %s: %d" % (
                format_client_message(address=remote_address,
                                      port=remote_port),
                connect.proto_level,
            )
            connack = ConnackPacket.build(0, UNACCEPTABLE_PROTOCOL_VERSION
                                          )  # [MQTT-3.2.2-4] session_parent=0
        elif not connect.username_flag and connect.password_flag:
            connack = ConnackPacket.build(
                0, BAD_USERNAME_PASSWORD)  # [MQTT-3.1.2-22]
        elif connect.username_flag and not connect.password_flag:
            connack = ConnackPacket.build(
                0, BAD_USERNAME_PASSWORD)  # [MQTT-3.1.2-22]
        elif connect.username_flag and connect.username is None:
            error_msg = "Invalid username from %s" % (format_client_message(
                address=remote_address, port=remote_port))
            connack = ConnackPacket.build(
                0, BAD_USERNAME_PASSWORD)  # [MQTT-3.2.2-4] session_parent=0
        elif connect.password_flag and connect.password is None:
            error_msg = "Invalid password %s" % (format_client_message(
                address=remote_address, port=remote_port))
            connack = ConnackPacket.build(
                0, BAD_USERNAME_PASSWORD)  # [MQTT-3.2.2-4] session_parent=0
        elif connect.clean_session_flag is False and (
                connect.payload.client_id_is_random):
            error_msg = (
                "[MQTT-3.1.3-8] [MQTT-3.1.3-9] %s: No client Id provided (cleansession=0)"
                % (format_client_message(address=remote_address,
                                         port=remote_port)))
            connack = ConnackPacket.build(0, IDENTIFIER_REJECTED)
        if connack is not None:
            logger.debug("B > %r", connack)
            await plugins_manager.fire_event(EVENT_MQTT_PACKET_SENT,
                                             packet=connack)
            await connack.to_stream(stream)

            await stream.close()
            raise MQTTException(error_msg)

        incoming_session = Session(plugins_manager)
        incoming_session.client_id = connect.client_id
        incoming_session.clean_session = connect.clean_session_flag
        incoming_session.will_flag = connect.will_flag
        incoming_session.will_retain = connect.will_retain_flag
        incoming_session.will_qos = connect.will_qos
        incoming_session.will_topic = connect.will_topic
        incoming_session.will_message = connect.will_message
        incoming_session.username = connect.username
        incoming_session.password = connect.password
        if connect.keep_alive > 0:
            incoming_session.keep_alive = connect.keep_alive
        else:
            incoming_session.keep_alive = 0

        handler = cls(plugins_manager)
        return handler, incoming_session
Example #7
0
def adapt(conn):
    return StreamAdapter(conn)