Esempio n. 1
0
 def communicate(self, raw: str, wait=True) -> str:
     eof = False
     response = ''
     data = raw.strip() + '\n'
     num_lines = data.count('\n')
     try:
         self._ssl_sock.sendall(data.encode('utf-8'))
         if not wait:
             return None
         while not eof:
             response += self._ssl_sock.recv().decode('utf-8')
             eof = response.count("\n") == num_lines or not response
         return response
     except socket.error as e:
         if 'read operation timed out' in str(e):
             raise Error("Timed out waiting for CLI response. "
                         "Perhaps the tunnel endpoint is incorrect, "
                         "or the LMS CLI is down?")
         else:
             print_d("Couldn't communicate with Squeezebox ({error!r})",
                     error=e)
         self.failures += 1
         if self.failures >= self._MAX_FAILURES:
             self.is_connected = False
             self._ssl_sock.close()
             raise Error("Too many Squeezebox failures. Disconnecting")
         return None
Esempio n. 2
0
 def connected(client, userdata, flags, rc):
     print_d("Connected to {client}. Subscribing to {topic}",
             client=self.client,
             topic=self.resp_topic)
     result, mid = self.client.subscribe(self.resp_topic, qos=1)
     if result != MQTT_ERR_SUCCESS:
         raise Error("Couldn't subscribe to '{topic}'", self.resp_topic)
Esempio n. 3
0
 def _conf_file_of(self, rel_glob: str) -> str:
     full_glob = os.path.join(self.settings.cert_dir, rel_glob)
     results = glob(full_glob)
     try:
         return results[0]
     except IndexError:
         raise Error("Can't find {glob} within dir {base}".format(
             base=self.settings.cert_dir, glob=rel_glob))
Esempio n. 4
0
    def _request(self, lines, raw=False, wait=True) -> List[str]:
        """
        Send multiple pipelined requests to the server, if connected,
        and return their responses,
        assuming order is maintained (which seems safe).
        """
        if not (lines and len(lines)):
            return []
        lines = [l.rstrip() for l in lines]

        match = RESPONSE_CMD_REGEX.match(lines[0])
        # If we can't match, then take the first two words (for debugging)
        first_word = (match.group(2)
                      if match else ' '.join(lines[0].split()[:2]))
        if not (self.transport.is_connected or first_word == 'login'):
            try:
                print_w("Transport wasn't connected - trying to restart")
                self.transport.start()
            except Exception:
                raise SqueezeboxException(
                    "Can't do '{cmd}', {transport} is not connected".format(
                        cmd=first_word, transport=self.transport))

        if self._debug:
            print_d("<<<< " + "\n..<< ".join(lines))
        request = "\n".join(lines)
        raw_response = self.transport.communicate(request, wait=wait)
        if not wait:
            return []
        if not raw_response:
            raise SqueezeboxException(
                "No further response from %s. Login problem?" % self)
        raw_response = raw_response.rstrip("\n")
        response = raw_response if raw else self._unquote(raw_response)
        if self._debug:
            print_d(">>>> " + "\n..>> ".join(response.splitlines()))

        def start_point(text):
            if first_word == 'login':
                return 6
            delta = -1 if text.endswith('?') else 1
            return len(self._unquote(text) if raw else text) + delta

        resp_lines = response.splitlines()
        if len(lines) != len(resp_lines):
            print_d("Got mismatched response: {lines} vs {resp_lines}",
                    lines=lines,
                    resp_lines=resp_lines)
            raise Error("Transport response problem: got %d lines, not %d" %
                        (len(resp_lines), len(lines)))
        return [
            resp_line[start_point(line):]
            for line, resp_line in zip(lines, resp_lines)
        ]
Esempio n. 5
0
    def connect(self, host=None, port=None, keepalive=30, bind_address=""):
        host = host or self.settings.hostname
        port = port or self.settings.port

        check_listening(host, port, msg="check your MQTT settings")
        ret = super().connect(host=host,
                              port=port,
                              keepalive=keepalive,
                              bind_address=bind_address)
        if MQTT_ERR_SUCCESS == ret:
            print_d("Connecting to {}", self.settings)
            return ret
        raise Error("Couldn't connect to {}".format(self.settings))
Esempio n. 6
0
    def connect(self, host=None, port=None, keepalive=30, bind_address=""):
        print_d("Connecting {client}...", client=self)
        host = host or self._host
        port = port or self._port

        check_listening(host, port, msg="check your MQTT settings")
        print_d("Remote socket is listening, let's continue.")
        try:
            ret = super().connect(host=host,
                                  port=port,
                                  keepalive=keepalive,
                                  bind_address=bind_address)
        except ssl.SSLError as e:
            if 'SSLV3_ALERT_CERTIFICATE_UNKNOWN' in str(e):
                raise Error("Certificate problem with MQTT. "
                            "Is the certificate enabled in AWS?")
        else:
            if ret == MQTT_ERR_SUCCESS:
                print_d("Connected to {settings}", settings=self.settings)
                self.connected = True
                return ret
        raise Error(
            "Couldn't connect to {settings}".format(settings=self.settings))
Esempio n. 7
0
    def communicate(self, raw: str, wait=True) -> str:
        data = raw.strip() + '\n'
        num_lines = data.count('\n')
        self._clear()
        ret = self.client.publish(self.req_topic,
                                  data.encode('utf-8'),
                                  qos=1 if wait else 0)
        if not wait:
            return None
        ret.wait_for_publish()
        if ret.rc != MQTT_ERR_SUCCESS:
            raise Error("Error publishing message: {}", error_string(ret.rc))
        print_d("Published to {topic} OK. Waiting for {num} line(s)...",
                topic=self.req_topic,
                num=num_lines)

        wait_for(lambda s: len(s.response_lines) >= num_lines,
                 context=self,
                 what="response from mqtt-squeeze",
                 timeout=5)
        return "\n".join(m.decode('utf-8') for m in self.response_lines)
Esempio n. 8
0
 def communicate(self, raw: str, wait=True) -> str:
     eof = False
     response = ''
     data = raw.strip() + '\n'
     num_lines = data.count('\n')
     try:
         self._ssl_sock.sendall(data.encode('utf-8'))
         if not wait:
             return None
         while not eof:
             response += self._ssl_sock.recv().decode('utf-8')
             eof = response.count("\n") == num_lines or not response
         return response
     except socket.error as e:
         print_d("Couldn't communicate with Squeezebox ({!r})", e)
         self.failures += 1
         if self.failures >= self._MAX_FAILURES:
             self.is_connected = False
             self._ssl_sock.close()
             raise Error("Too many Squeezebox failures. Disconnecting")
         return None
Esempio n. 9
0
    def __init__(self,
                 hostname,
                 port=9090,
                 ca_file=None,
                 cert_file=None,
                 verify_hostname=False,
                 timeout=5):

        super().__init__()
        self.hostname = hostname
        self.port = port
        self.timeout = timeout
        self.failures = 0
        context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
        self.__harden_context(context)
        try:
            if ca_file:
                context.load_verify_locations(ca_file)
            if cert_file:
                context.verify_mode = ssl.CERT_REQUIRED
                context.check_hostname = verify_hostname
                context.load_cert_chain(cert_file)
        except ssl.SSLError as e:
            raise Error(
                "Problem with Cert / CA (+key) files ({} / {}). "
                "Does it include the private key?".format(cert_file, ca_file),
                e)
        except IOError as e:
            if 'No such file or directory' in e.strerror:
                self._die("Can't find cert '{cert_file}' or CA '{ca_file}'. "
                          "Check CERT_FILE / CA_FILE_PATH in settings".format(
                              ca_file=ca_file, cert_file=cert_file))
            self._die(
                "could be mismatched certificate files, "
                "or wrong hostname in cert."
                "Check CERT_FILE and certs on server too.", e)

        sock = socket.socket()
        sock.settimeout(self.timeout)
        self._ssl_sock = context.wrap_socket(sock, server_hostname=hostname)
        print_d("Connecting to port {port} on {hostname}",
                port=port,
                hostname=hostname or '(localhost)')
        try:
            self._ssl_sock.connect((hostname, port))
        except socket.gaierror as e:
            if "Name or service not know" in e.strerror:
                self._die(
                    "unknown host ({}) - check SERVER_HOSTNAME".format(
                        hostname), e)
            self._die("Couldn't connect to %s with TLS" % (self, ), e)
        except IOError as e:
            err_str = e.strerror or str(e)
            if 'Connection refused' in err_str:
                self._die("nothing listening on {}. "
                          "Check settings, or (re)start server.".format(self))
            elif 'WRONG_VERSION_NUMBER' in err_str:
                self._die(
                    'probably not TLS on port {} - '
                    'wrong SERVER_PORT maybe?'.format(port), e)
            elif 'Connection reset by peer' in err_str:
                self._die("server killed the connection - handshake error? "
                          "Check the SSL tunnel logs")
            elif 'CERTIFICATE_VERIFY_FAILED' in err_str:
                self._die(
                    "Cert not trusted by / from server. "
                    "Is your CA correct? Is the cert expired? "
                    "Is the cert for the right hostname ({})?".format(
                        hostname), e)
            elif 'timed out' in err_str:
                msg = ("Couldn't connect to port {port} on {host} - "
                       "check the server setup and the firewall.").format(
                           host=self.hostname, port=self.port)
                self._die(msg)
            self._die("Connection problem ({}: {})".format(
                type(e).__name__, err_str))

        peer_cert = self._ssl_sock.getpeercert()
        if peer_cert is None:
            self._die("No certificate configured at {}".format(self))
        elif not peer_cert:
            print_w("Unvalidated server cert at {}", self)
        else:
            subject_data = peer_cert['subject']
            try:
                data = {k: v for d in subject_data for k, v in d}
            except Exception:
                data = subject_data
            print_d("Validated cert for {}", data)
        self.is_connected = True
Esempio n. 10
0
 def _die(self, msg, e=None):
     raise Error(msg, e)
Esempio n. 11
0
 def disconnect(self):
     ret = super().disconnect()
     self.connected = False
     if ret != MQTT_ERR_SUCCESS and ret != MQTT_ERR_NO_CONN:
         raise Error("Failed to disconnect (%s)" % error_string(ret))
     return ret
Esempio n. 12
0
 def _die(self, msg, err=None, **kwargs):
     raise Error(msg.format(**kwargs), err)