Skip to content

Commit

Permalink
Prevent invalid window bit sizes
Browse files Browse the repository at this point in the history
This is specifically to deal with the case whereby the window size is
set to 8. As zlib dropped support for 256 (8 bit) window sizes in
1.2.9, https://www.zlib.net/ChangeLog.txt, onwards. 8 bit windows were
broken before as well, madler/zlib#171. As
autobahn tests 8 bit windows the tox test would fail based on the zlib
version installed - hence the relevant autobahn tests are also
disabled.

8 bit windows are rarely used in practice, so this should be ok
practically. If not zlib needs to be fixed, see
madler/zlib#87 (comment)
  • Loading branch information
pgjones committed Feb 16, 2020
1 parent 07723dc commit 70c88f0
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 21 deletions.
4 changes: 2 additions & 2 deletions compliance/run-autobahn-tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
}
],
"cases": ["*"],
"exclude-cases": [],
"exclude-cases": ["13.3.*", "13.5.*", "13.7.*"],
"exclude-agent-cases": {},
}

Expand All @@ -35,7 +35,7 @@
"outdir": "./reports/clients",
"webport": 8080,
"cases": ["*"],
"exclude-cases": [],
"exclude-cases": ["13.3.*", "13.5.*", "13.7.*"],
"exclude-agent-cases": {},
}

Expand Down
56 changes: 39 additions & 17 deletions src/wsproto/extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,13 @@ def __init__(
server_max_window_bits: Optional[int] = None,
) -> None:
self.client_no_context_takeover = client_no_context_takeover
if client_max_window_bits is None:
client_max_window_bits = self.DEFAULT_CLIENT_MAX_WINDOW_BITS
self.client_max_window_bits = client_max_window_bits
self.server_no_context_takeover = server_no_context_takeover
if server_max_window_bits is None:
server_max_window_bits = self.DEFAULT_SERVER_MAX_WINDOW_BITS
self.server_max_window_bits = server_max_window_bits
self._client_max_window_bits = self.DEFAULT_CLIENT_MAX_WINDOW_BITS
self._server_max_window_bits = self.DEFAULT_SERVER_MAX_WINDOW_BITS
if client_max_window_bits is not None:
self.client_max_window_bits = client_max_window_bits
if server_max_window_bits is not None:
self.server_max_window_bits = server_max_window_bits

self._compressor: Optional[zlib._Compress] = None # noqa
self._decompressor: Optional[zlib._Decompress] = None # noqa
Expand All @@ -90,6 +90,26 @@ def __init__(

self._enabled = False

@property
def client_max_window_bits(self) -> int:
return self._client_max_window_bits

@client_max_window_bits.setter
def client_max_window_bits(self, value: int) -> None:
if value < 9 or value > 15:
raise ValueError("Window size must be between 9 and 15 inclusive")
self._client_max_window_bits = value

@property
def server_max_window_bits(self) -> int:
return self._server_max_window_bits

@server_max_window_bits.setter
def server_max_window_bits(self, value: int) -> None:
if value < 9 or value > 15:
raise ValueError("Window size must be between 9 and 15 inclusive")
self._server_max_window_bits = value

def _compressible_opcode(self, opcode: Opcode) -> bool:
return opcode in (Opcode.TEXT, Opcode.BINARY, Opcode.CONTINUATION)

Expand Down Expand Up @@ -146,25 +166,27 @@ def _parse_params(self, params: str) -> Tuple[Optional[int], Optional[int]]:

return client_max_window_bits, server_max_window_bits

def accept(self, offer: str) -> Union[bool, str]:
def accept(self, offer: str) -> Union[bool, None, str]:
client_max_window_bits, server_max_window_bits = self._parse_params(offer)

self._enabled = True

parameters = []

if self.client_no_context_takeover:
parameters.append("client_no_context_takeover")
if client_max_window_bits is not None:
parameters.append("client_max_window_bits=%d" % client_max_window_bits)
self.client_max_window_bits = client_max_window_bits
if self.server_no_context_takeover:
parameters.append("server_no_context_takeover")
if server_max_window_bits is not None:
parameters.append("server_max_window_bits=%d" % server_max_window_bits)
self.server_max_window_bits = server_max_window_bits

return "; ".join(parameters)
try:
if client_max_window_bits is not None:
parameters.append("client_max_window_bits=%d" % client_max_window_bits)
self.client_max_window_bits = client_max_window_bits
if server_max_window_bits is not None:
parameters.append("server_max_window_bits=%d" % server_max_window_bits)
self.server_max_window_bits = server_max_window_bits
except ValueError:
return None
else:
self._enabled = True
return "; ".join(parameters)

def frame_inbound_header(
self,
Expand Down
4 changes: 2 additions & 2 deletions test/test_permessage_deflate.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ class TestPerMessageDeflate:
},
{
"client_no_context_takeover": True,
"client_max_window_bits": 8,
"client_max_window_bits": 9,
"server_no_context_takeover": True,
"server_max_window_bits": 9,
},
{"client_no_context_takeover": True, "server_max_window_bits": 9},
{"server_no_context_takeover": True, "client_max_window_bits": 8},
{"server_no_context_takeover": True, "client_max_window_bits": 9},
{"client_max_window_bits": None, "server_max_window_bits": None},
{},
]
Expand Down

0 comments on commit 70c88f0

Please sign in to comment.