From 0772fb7382493905f3e4276dccc2a15f4cf0d2ea Mon Sep 17 00:00:00 2001 From: Eapp1e <163811520+Eapp1e@users.noreply.github.com> Date: Sun, 25 Oct 2026 14:27:07 +0000 Subject: [PATCH] contrib/tcpros: read field lengths in place instead of re-slicing guess_payload_class() and both pre_dissect() loops re-sliced the remaining buffer for every header field, so dissecting a header with N fields copied the whole remainder N times. Add tcpros_iter_field_lengths(), which unpacks each length with struct.unpack_from() and advances an offset, and use it in the three places that walked the field list. The end-of-buffer check in guess_payload_class() becomes an offset comparison instead of comparing the declared length with the length of a freshly built slice. Cover the traversal, the offset start, a 300-field header and the conf.debug_dissector output in test/contrib/tcpros.uts. --- scapy/contrib/tcpros.py | 50 ++++++++++++++++++++++++----------------- test/contrib/tcpros.uts | 39 ++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 20 deletions(-) diff --git a/scapy/contrib/tcpros.py b/scapy/contrib/tcpros.py index 90773d32a7b..ec0dcefc2b7 100644 --- a/scapy/contrib/tcpros.py +++ b/scapy/contrib/tcpros.py @@ -25,6 +25,28 @@ from scapy.config import conf +def tcpros_iter_field_lengths(data, start=0): + """Iterate over the lengths of the ``[4-byte length + value]`` fields. + + The fields are read in place with ``struct.unpack_from``: slicing the + buffer for every field copies the whole remainder, which makes the + traversal quadratic in the number of fields. + + Args: + data: buffer holding the field list. + start: offset of the first field length in ``data``. + + Yields: + the length of each field, in order. + """ + end = len(data) + offset = start + while offset < end: + field_len = struct.unpack_from(" len(payload) - offset - 4: # print("BREAKING - int(field_len_bytes) != len(current)") flag_encoding_format_subfields = False break + offset += 4 + field_len if ( "callerid" in string_payload and @@ -214,14 +234,9 @@ def pre_dissect(self, s): print("total_length: " + str(total_length)) print("total_header_length: " + str(total_header_length)) remain = raw(s)[4:total_header_length] - while remain: - field_len_bytes = struct.unpack("\n") assert(p[XMLRPCCall].methodcall_opentag == b'\n') assert(p[XMLRPCCall].methodname == b'shutdown') assert(p[XMLRPCCall].params == b'\n/rosparam-92418\n\n\nBOOM\n\n') + ++ Test TCPROS field list parsing += Test field lengths traversal +from struct import pack +from scapy.contrib.tcpros import tcpros_iter_field_lengths + +field_list = pack("