From 2ee60783b694b6a8555a6afbef21dc1f2ca1f8b9 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Sat, 16 May 2015 11:31:53 +1200 Subject: [PATCH] websockets: client and server flavors, key and mask specification --- libpathod/language/base.py | 2 + libpathod/language/http.py | 2 +- libpathod/language/websockets.py | 49 +++++++++++------ libpathod/pathoc.py | 3 +- libpathod/templates/docs_lang_websockets.html | 16 +++++- test/test_language_websocket.py | 53 +++++++++++++++---- test/tutils.py | 1 + 7 files changed, 96 insertions(+), 30 deletions(-) diff --git a/libpathod/language/base.py b/libpathod/language/base.py index 41ad639a2..3773fde1a 100644 --- a/libpathod/language/base.py +++ b/libpathod/language/base.py @@ -10,6 +10,7 @@ from . import generators, exceptions class Settings: def __init__( self, + is_client = False, staticdir = None, unconstrained_file_access = False, request_host = None, @@ -19,6 +20,7 @@ class Settings: self.unconstrained_file_access = unconstrained_file_access self.request_host = request_host self.websocket_key = websocket_key + self.is_client = is_client diff --git a/libpathod/language/http.py b/libpathod/language/http.py index 94de7237e..070cc5f44 100644 --- a/libpathod/language/http.py +++ b/libpathod/language/http.py @@ -5,7 +5,7 @@ import pyparsing as pp import netlib.websockets from netlib import http_status, http_uastrings -from . import base, generators, exceptions, actions, message +from . import base, exceptions, actions, message class WS(base.CaselessLiteral): diff --git a/libpathod/language/websockets.py b/libpathod/language/websockets.py index 3abdf9d81..599cdb886 100644 --- a/libpathod/language/websockets.py +++ b/libpathod/language/websockets.py @@ -1,4 +1,4 @@ - +import os import netlib.websockets import pyparsing as pp from . import base, generators, actions, message @@ -115,6 +115,10 @@ class WebsocketFrame(message.Message): def mask(self): return self.tok(Mask) + @property + def key(self): + return self.tok(Key) + @classmethod def expr(klass): parts = [i.expr() for i in klass.comps] @@ -129,8 +133,21 @@ class WebsocketFrame(message.Message): resp = resp.setParseAction(klass) return resp + def resolve(self, settings, msg=None): + tokens = self.tokens[:] + if not self.mask and settings.is_client: + tokens.append( + Mask(True) + ) + if self.mask and self.mask.value and not self.key: + tokens.append( + Key(base.TokValueLiteral(os.urandom(4))) + ) + return self.__class__( + [i.resolve(settings, self) for i in tokens] + ) + def values(self, settings): - vals = [] if self.body: bodygen = self.body.value.get_generator(settings) length = len(self.body.value.get_generator(settings)) @@ -138,29 +155,31 @@ class WebsocketFrame(message.Message): bodygen = None length = 0 frameparts = dict( - mask = True, payload_length = length ) + if self.mask and self.mask.value: + frameparts["mask"] = True + if self.key: + key = self.key.values(settings)[0][:] + frameparts["masking_key"] = key for i in ["opcode", "fin", "rsv1", "rsv2", "rsv3", "mask"]: v = getattr(self, i, None) if v is not None: frameparts[i] = v.value frame = netlib.websockets.FrameHeader(**frameparts) vals = [frame.to_bytes()] - if self.body: - masker = netlib.websockets.Masker(frame.masking_key) - vals.append( - generators.TransformGenerator( - bodygen, - masker.mask + if bodygen: + if frame.masking_key: + masker = netlib.websockets.Masker(frame.masking_key) + vals.append( + generators.TransformGenerator( + bodygen, + masker.mask + ) ) - ) + else: + vals.append(bodygen) return vals - def resolve(self, settings, msg=None): - return self.__class__( - [i.resolve(settings, self) for i in self.tokens] - ) - def spec(self): return ":".join([i.spec() for i in self.tokens]) diff --git a/libpathod/pathoc.py b/libpathod/pathoc.py index 2574da6c1..3d61c9e7d 100644 --- a/libpathod/pathoc.py +++ b/libpathod/pathoc.py @@ -212,7 +212,8 @@ class Pathoc(tcp.TCPClient): self.settings = language.Settings( staticdir = os.getcwd(), unconstrained_file_access = True, - request_host = self.address.host + request_host = self.address.host, + is_client = True ) self.ssl, self.sni = ssl, sni self.clientcert = clientcert diff --git a/libpathod/templates/docs_lang_websockets.html b/libpathod/templates/docs_lang_websockets.html index c50d081f0..9eb1ec25e 100644 --- a/libpathod/templates/docs_lang_websockets.html +++ b/libpathod/templates/docs_lang_websockets.html @@ -44,6 +44,14 @@ + + kVALUE + + Set the masking key. The resulting value must be exactly 4 + bytes long. + + + [-]mask @@ -62,8 +70,12 @@ r - Create a "raw" frame - disables auto-generation of the masking - key if the mask bit is on. + Create a "raw" frame: +