From 7b21caceaec36b3bb7950fdff3dc99895db1e389 Mon Sep 17 00:00:00 2001 From: croneter Date: Tue, 5 Feb 2019 12:37:01 +0100 Subject: [PATCH 1/2] Allow websocket redirects --- resources/lib/websocket.py | 20 ++++++++++++-- resources/lib/websocket_client.py | 45 ++++++++++++++++++++++--------- 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/resources/lib/websocket.py b/resources/lib/websocket.py index 7c4e3452..8f9e372b 100644 --- a/resources/lib/websocket.py +++ b/resources/lib/websocket.py @@ -105,6 +105,19 @@ class WebSocketTimeoutException(WebSocketException): pass +class WebsocketRedirect(WebSocketException): + """ + WebsocketRedirect will be raised if a status code 301 is returned + The Exception will be instantiated with a dict containing all response + headers; which should contain the redirect address under the key 'location' + + Access the headers via the attribute headers + """ + def __init__(self, headers): + self.headers = headers + super(WebsocketRedirect, self).__init__() + + DEFAULT_TIMEOUT = None TRACE_ENABLED = False @@ -162,10 +175,10 @@ def _parse_url(url): port = parsed.port is_secure = False - if scheme == "ws": + if scheme == "ws" or scheme == 'http': if not port: port = 80 - elif scheme == "wss": + elif scheme == "wss" or scheme == 'https': is_secure = True if not port: port = 443 @@ -500,6 +513,9 @@ class WebSocket(object): LOG.debug("-----------------------") status, resp_headers = self._read_headers() + if status == 301: + # Redirect + raise WebsocketRedirect(resp_headers) if status != 101: self.close() raise WebSocketException("Handshake Status %d" % status) diff --git a/resources/lib/websocket_client.py b/resources/lib/websocket_client.py index 2068d2f8..4a2d255b 100644 --- a/resources/lib/websocket_client.py +++ b/resources/lib/websocket_client.py @@ -18,6 +18,7 @@ class WebSocket(backgroundthread.KillableThread): def __init__(self): self.ws = None + self.redirect_uri = None super(WebSocket, self).__init__() def process(self, opcode, message): @@ -91,6 +92,16 @@ class WebSocket(backgroundthread.KillableThread): self.__class__.__name__) self.ws = None app.APP.monitor.waitForAbort(1) + except websocket.WebsocketRedirect as e: + LOG.info('301 redirect detected') + self.redirect_uri = e.headers.get('location', e.headers.get('Location')) + if self.redirect_uri: + self.redirect_uri.decode('utf-8') + counter += 1 + if counter >= 10: + LOG.info('%s: Repeated WebsocketRedirect detected. Stopping now', + self.__class__.__name__) + break except websocket.WebSocketException as e: LOG.info('%s: WebSocketException: %s', self.__class__.__name__, e) @@ -141,16 +152,20 @@ class PMS_Websocket(WebSocket): app.SYNC.background_sync_disabled) def getUri(self): - server = app.CONN.server - # Get the appropriate prefix for the websocket - if server.startswith('https'): - server = "wss%s" % server[5:] + if self.redirect_uri: + uri = self.redirect_uri + self.redirect_uri = None else: - server = "ws%s" % server[4:] - uri = "%s/:/websockets/notifications" % server - # Need to use plex.tv token, if any. NOT user token - if app.ACCOUNT.plex_token: - uri += '?X-Plex-Token=%s' % app.ACCOUNT.plex_token + server = app.CONN.server + # Get the appropriate prefix for the websocket + if server.startswith('https'): + server = "wss%s" % server[5:] + else: + server = "ws%s" % server[4:] + uri = "%s/:/websockets/notifications" % server + # Need to use plex.tv token, if any. NOT user token + if app.ACCOUNT.plex_token: + uri += '?X-Plex-Token=%s' % app.ACCOUNT.plex_token sslopt = {} if utils.settings('sslverify') == "false": sslopt["cert_reqs"] = CERT_NONE @@ -209,10 +224,14 @@ class Alexa_Websocket(WebSocket): app.ACCOUNT.restricted_user) def getUri(self): - uri = ('wss://pubsub.plex.tv/sub/websockets/%s/%s?X-Plex-Token=%s' - % (app.ACCOUNT.plex_user_id, - v.PKC_MACHINE_IDENTIFIER, - app.ACCOUNT.plex_token)) + if self.redirect_uri: + uri = self.redirect_uri + self.redirect_uri = None + else: + uri = ('wss://pubsub.plex.tv/sub/websockets/%s/%s?X-Plex-Token=%s' + % (app.ACCOUNT.plex_user_id, + v.PKC_MACHINE_IDENTIFIER, + app.ACCOUNT.plex_token)) sslopt = {} LOG.debug("%s: Uri: %s, sslopt: %s", self.__class__.__name__, uri, sslopt) From 0e16eb703a5b6d31965fe0d091e63ad61adc5e15 Mon Sep 17 00:00:00 2001 From: croneter Date: Tue, 5 Feb 2019 12:38:53 +0100 Subject: [PATCH 2/2] Do not allow insecure HTTPS websocket connections for Kodi Leia in any case --- resources/lib/websocket_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lib/websocket_client.py b/resources/lib/websocket_client.py index 4a2d255b..8ed95d16 100644 --- a/resources/lib/websocket_client.py +++ b/resources/lib/websocket_client.py @@ -167,7 +167,7 @@ class PMS_Websocket(WebSocket): if app.ACCOUNT.plex_token: uri += '?X-Plex-Token=%s' % app.ACCOUNT.plex_token sslopt = {} - if utils.settings('sslverify') == "false": + if v.KODIVERSION == 17 and utils.settings('sslverify') == "false": sslopt["cert_reqs"] = CERT_NONE LOG.debug("%s: Uri: %s, sslopt: %s", self.__class__.__name__, uri, sslopt)