Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Unreleased

* `get_string` and `get_wstring` now raise `ValueError` when the buffer is shorter than the declared current length,
instead of silently returning a truncated value (#945).
* USERDATA responses whose last-data-unit byte is neither `0x00` nor `0x01` are rejected with `S7ProtocolError`
instead of being treated as "more data" (#946).
* `get_wstring` compared the declared maximum in bytes with the 16382-character limit, so any WSTRING with a
capacity of 8192 characters or more was rejected, including ones written by `set_wstring` (#923).
* The get-block-info request now places the trailing `A` after the block number, as native Snap7 does
Expand Down
4 changes: 3 additions & 1 deletion snap7/s7protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -1460,6 +1460,8 @@ def check_userdata_response(
if param_error != 0:
error_msg = get_protocol_error_message(param_error)
raise S7ProtocolError(f"USERDATA request failed: {error_msg} (0x{param_error:04x})")
if params.get("last_data_unit", 0) not in (0x00, 0x01):
raise S7ProtocolError(f"Invalid USERDATA last data unit value 0x{params['last_data_unit']:02x}")
if expected_group is not None and params.get("group") != expected_group:
raise S7ProtocolError("Unexpected USERDATA response function group")
if expected_subfunction is not None and params.get("subfunction") != expected_subfunction:
Expand Down Expand Up @@ -1689,7 +1691,7 @@ def _parse_userdata_response_params(self, param_data: bytes) -> Dict[str, Any]:
[6] Subfunction
[7] Sequence number (used as DataRef in follow-up)
[8] Data unit reference
[9] Last data unit (0x00 = last, non-zero = more)
[9] Last data unit (0x00 = last, 0x01 = more)
[10-11] Error code
"""
type_group = param_data[5]
Expand Down
10 changes: 10 additions & 0 deletions tests/test_multipacket.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,3 +442,13 @@ def test_tpdu_size_in_cotp_cr(self) -> None:
conn = ISOTCPConnection("127.0.0.1", tpdu_size=TPDUSize.S_2048)
cr_pdu = conn._build_cotp_cr()
assert cr_pdu[-3:] == bytes([0xC0, 0x01, TPDUSize.S_2048])


@pytest.mark.parametrize("last", [0x02, 0xFF])
def test_userdata_invalid_last_data_unit_rejected(last: int) -> None:
"""Only 0x00 and 0x01 are valid last-data-unit values."""
params = bytes([0, 1, 0x12, 8, 0x12, 0x84, 1, 1, 0, last, 0, 0])
data = bytes([0xFF, 9, 0, 4]) + b"abcd"
pdu = struct.pack(">BBHHHH", 0x32, 7, 0, 1, len(params), len(data)) + params + data
with pytest.raises(S7ProtocolError):
S7Protocol().parse_response(pdu)
Loading