Esempio n. 1
0
    def _handle_tls_args(self):
        """Make sure TLS options are valid
        """

        if self._tls_args:
            # If _any_ options are specified, first assume we DO want it enabled
            self._enable_tls = True
        else:
            self._enable_tls = False
            return

        if "enable" in self._tls_args:
            if not self._tls_args.pop("enable"):
                # if enable=false, return immediately
                self._enable_tls = False
                return

        if "keyfile" in self._tls_args and "certfile" not in self._tls_args:
            raise exceptions.MQTTTLSError(
                "If specifying a TLS keyfile, a certfile also needs to be specified"
            )

        def check_file_exists(key):
            try:
                with open(self._tls_args[key], "r"):
                    pass
            except LoadError as e:
                raise_from(
                    exceptions.MQTTTLSError(
                        "Couldn't load '{}' from '{}'".format(
                            key, self._tls_args[key])), e)
            except KeyError:
                pass

        # could be moved to schema validation stage
        check_file_exists("cert_reqs")
        check_file_exists("certfile")
        check_file_exists("keyfile")

        # This shouldn't raise an AttributeError because it's enumerated
        try:
            self._tls_args["cert_reqs"] = getattr(ssl,
                                                  self._tls_args["cert_reqs"])
        except KeyError:
            pass

        try:
            self._tls_args["tls_version"] = getattr(
                ssl, self._tls_args["tls_version"])
        except AttributeError as e:
            raise_from(
                exceptions.MQTTTLSError(
                    "Error getting TLS version from "
                    "ssl module - ssl module had no attribute '{}'. Check the "
                    "documentation for the version of python you're using to see "
                    "if this a valid option.".format(
                        self._tls_args["tls_version"])), e)
        except KeyError:
            pass
Esempio n. 2
0
def _handle_tls_args(tls_args):
    """Make sure TLS options are valid
    """

    if not tls_args:
        return None

    if "enable" in tls_args:
        if not tls_args.pop("enable"):
            # if enable=false, return immediately
            return None

    if "keyfile" in tls_args and "certfile" not in tls_args:
        raise exceptions.MQTTTLSError(
            "If specifying a TLS keyfile, a certfile also needs to be specified"
        )

    def check_file_exists(key):
        try:
            with open(tls_args[key], "r", encoding="utf-8"):
                pass
        except IOError as e:
            raise exceptions.MQTTTLSError(
                "Couldn't load '{}' from '{}'".format(key, tls_args[key])
            ) from e
        except KeyError:
            pass

    # could be moved to schema validation stage
    check_file_exists("cert_reqs")
    check_file_exists("certfile")
    check_file_exists("keyfile")

    # This shouldn't raise an AttributeError because it's enumerated
    try:
        tls_args["cert_reqs"] = getattr(ssl, tls_args["cert_reqs"])
    except KeyError:
        pass

    try:
        tls_args["tls_version"] = getattr(ssl, tls_args["tls_version"])
    except AttributeError as e:
        raise exceptions.MQTTTLSError(
            "Error getting TLS version from "
            "ssl module - ssl module had no attribute '{}'. Check the "
            "documentation for the version of python you're using to see "
            "if this a valid option.".format(tls_args["tls_version"])
        ) from e
    except KeyError:
        pass

    return tls_args
Esempio n. 3
0
 def check_file_exists(key):
     try:
         with open(self._tls_args[key], "r"):
             pass
     except LoadError as e:
         raise_from(exceptions.MQTTTLSError("Couldn't load '{}' from '{}'".format(
             key, self._tls_args[key])), e)
     except KeyError:
         pass
Esempio n. 4
0
 def check_file_exists(key):
     try:
         with open(tls_args[key], "r", encoding="utf-8"):
             pass
     except IOError as e:
         raise exceptions.MQTTTLSError(
             "Couldn't load '{}' from '{}'".format(key,
                                                   tls_args[key])) from e
     except KeyError:
         pass
Esempio n. 5
0
    def __init__(self, **kwargs):
        expected_blocks = {
            "client": {
                "client_id",
                "clean_session",
                # Can't really use this easily...
                # "userdata",
                # Force mqttv311 - fix if this becomes an issue
                # "protocol",
                "transport",
            },
            "connect": {
                "host",
                "port",
                "keepalive",
                "timeout",
            },
            "tls": {
                "enable",
                "ca_certs",
                "cert_reqs",
                "certfile",
                "keyfile",
                "tls_version",
                "ciphers",
            },
            "auth": {
                "username",
                "password",
            },
        }

        logger.debug("Initialising MQTT client with %s", kwargs)

        # check main block first
        check_expected_keys(expected_blocks.keys(), kwargs)

        # then check constructor/connect/tls_set args
        self._client_args = kwargs.pop("client", {})
        check_expected_keys(expected_blocks["client"], self._client_args)

        self._connect_args = kwargs.pop("connect", {})
        check_expected_keys(expected_blocks["connect"], self._connect_args)

        self._auth_args = kwargs.pop("auth", {})
        check_expected_keys(expected_blocks["auth"], self._auth_args)

        if "host" not in self._connect_args:
            msg = "Need 'host' in 'connect' block for mqtt"
            logger.error(msg)
            raise exceptions.MissingKeysError(msg)

        self._connect_timeout = self._connect_args.pop("timeout", 3)

        # If there is any tls kwarg (including 'enable'), enable tls
        self._tls_args = kwargs.pop("tls", {})
        check_expected_keys(expected_blocks["tls"], self._tls_args)
        self._handle_tls_args()
        logger.debug("TLS is %s",
                     "enabled" if self._enable_tls else "disabled")

        logger.debug("Paho client args: %s", self._client_args)
        self._client = paho.Client(**self._client_args)
        self._client.enable_logger()

        if self._auth_args:
            self._client.username_pw_set(**self._auth_args)

        self._client.on_message = self._on_message

        if self._enable_tls:
            try:
                self._client.tls_set(**self._tls_args)
            except ValueError as e:
                # tls_set only raises ValueErrors directly
                raise_from(
                    exceptions.MQTTTLSError("Unexpected error enabling TLS",
                                            e))
            except ssl.SSLError as e:
                # incorrect cipher, etc.
                raise_from(
                    exceptions.MQTTTLSError(
                        "Unexpected SSL error enabling TLS", e))

        # Arbitrary number, could just be 1 and only accept 1 message per stages
        # but we might want to raise an error if more than 1 message is received
        # during a test stage.
        self._message_queue = Queue(maxsize=10)
        self._userdata = {
            "queue": self._message_queue,
        }
        self._client.user_data_set(self._userdata)