From 155cbe44f73c692090cf4b5e5cc5ad90d5eb615a Mon Sep 17 00:00:00 2001 From: Nils Weiss Date: Fri, 11 Aug 2023 20:56:15 +0200 Subject: [PATCH 1/9] add more precise parsing of DTCs add more precise parsing of DTCs --- scapy/contrib/automotive/uds.py | 87 +++++++++++++-------------------- test/contrib/automotive/uds.uts | 29 +++++------ 2 files changed, 46 insertions(+), 70 deletions(-) diff --git a/scapy/contrib/automotive/uds.py b/scapy/contrib/automotive/uds.py index bbe0316144c..0bd2343288d 100644 --- a/scapy/contrib/automotive/uds.py +++ b/scapy/contrib/automotive/uds.py @@ -16,8 +16,7 @@ BitEnumField, BitField, XByteField, FieldListField, \ XShortField, X3BytesField, XIntField, ByteField, \ ShortField, ObservableDict, XShortEnumField, XByteEnumField, StrLenField, \ - FieldLenField, XStrFixedLenField, XStrLenField, FlagsField, PacketListField, \ - PacketField + FieldLenField, XStrFixedLenField, XStrLenField, FlagsField, PacketListField from scapy.packet import Packet, bind_layers, NoPayload from scapy.config import conf from scapy.error import log_loading @@ -153,7 +152,7 @@ class UDS_DSCPR(Packet): def answers(self, other): return isinstance(other, UDS_DSC) and \ - other.diagnosticSessionType == self.diagnosticSessionType + other.diagnosticSessionType == self.diagnosticSessionType bind_layers(UDS, UDS_DSCPR, service=0x50) @@ -219,7 +218,7 @@ class UDS_SAPR(Packet): def answers(self, other): return isinstance(other, UDS_SA) \ - and other.securityAccessType == self.securityAccessType + and other.securityAccessType == self.securityAccessType bind_layers(UDS, UDS_SAPR, service=0x67) @@ -274,7 +273,7 @@ class UDS_CCPR(Packet): def answers(self, other): return isinstance(other, UDS_CC) \ - and other.controlType == self.controlType + and other.controlType == self.controlType bind_layers(UDS, UDS_CCPR, service=0x68) @@ -430,7 +429,7 @@ class UDS_AUTHPR(Packet): def answers(self, other): return isinstance(other, UDS_AUTH) \ - and other.subFunction == self.subFunction + and other.subFunction == self.subFunction bind_layers(UDS, UDS_AUTHPR, service=0x69) @@ -492,8 +491,8 @@ class UDS_ATPPR(Packet): def answers(self, other): return isinstance(other, UDS_ATP) \ - and other.timingParameterAccessType == \ - self.timingParameterAccessType + and other.timingParameterAccessType == \ + self.timingParameterAccessType bind_layers(UDS, UDS_ATPPR, service=0xC3) @@ -606,7 +605,7 @@ class UDS_ROEPR(Packet): def answers(self, other): return isinstance(other, UDS_ROE) \ - and other.eventType == self.eventType + and other.eventType == self.eventType bind_layers(UDS, UDS_ROEPR, service=0xC6) @@ -645,7 +644,7 @@ class UDS_LCPR(Packet): def answers(self, other): return isinstance(other, UDS_LC) \ - and other.linkControlType == self.linkControlType + and other.linkControlType == self.linkControlType bind_layers(UDS, UDS_LCPR, service=0xC7) @@ -674,7 +673,7 @@ class UDS_RDBIPR(Packet): def answers(self, other): return isinstance(other, UDS_RDBI) \ - and self.dataIdentifier in other.identifiers + and self.dataIdentifier in other.identifiers bind_layers(UDS, UDS_RDBIPR, service=0x62) @@ -744,7 +743,7 @@ class UDS_RSDBIPR(Packet): def answers(self, other): return isinstance(other, UDS_RSDBI) \ - and other.dataIdentifier == self.dataIdentifier + and other.dataIdentifier == self.dataIdentifier bind_layers(UDS, UDS_RSDBIPR, service=0x64) @@ -781,7 +780,7 @@ class UDS_RDBPIPR(Packet): def answers(self, other): return isinstance(other, UDS_RDBPI) \ - and other.periodicDataIdentifier == self.periodicDataIdentifier + and other.periodicDataIdentifier == self.periodicDataIdentifier bind_layers(UDS, UDS_RDBPIPR, service=0x6A) @@ -813,7 +812,7 @@ class UDS_DDDIPR(Packet): def answers(self, other): return isinstance(other, UDS_DDDI) \ - and other.subFunction == self.subFunction + and other.subFunction == self.subFunction bind_layers(UDS, UDS_DDDIPR, service=0x6C) @@ -840,7 +839,7 @@ class UDS_WDBIPR(Packet): def answers(self, other): return isinstance(other, UDS_WDBI) \ - and other.dataIdentifier == self.dataIdentifier + and other.dataIdentifier == self.dataIdentifier bind_layers(UDS, UDS_WDBIPR, service=0x6E) @@ -901,8 +900,8 @@ class UDS_WMBAPR(Packet): def answers(self, other): return isinstance(other, UDS_WMBA) \ - and other.memorySizeLen == self.memorySizeLen \ - and other.memoryAddressLen == self.memoryAddressLen + and other.memorySizeLen == self.memorySizeLen \ + and other.memoryAddressLen == self.memoryAddressLen bind_layers(UDS, UDS_WMBAPR, service=0x7D) @@ -1012,32 +1011,12 @@ class UDS_RDTCI(Packet): class DTC(Packet): - name = 'Diagnostic Trouble Code' - dtc_descriptions = {} # Customize this dictionary for each individual ECU / OEM - + name = 'DTC and status record' fields_desc = [ - BitEnumField("system", 0, 2, { - 0: "Powertrain", - 1: "Chassis", - 2: "Body", - 3: "Network"}), - BitEnumField("type", 0, 2, { - 0: "Generic", - 1: "ManufacturerSpecific", - 2: "Generic", - 3: "Generic"}), + BitEnumField("system", 0, 2, {0: "Powertrain", 1: "Chassis", 2: "Body", 3: "Network"}), + BitEnumField("type", 0, 2, {0: "Generic", 1: "ManufacturerSpecific", 2: "Generic", 3: "Generic"}), BitField("numeric_value_code", 0, 12), ByteField("additional_information_code", 0), - ] - - def extract_padding(self, s): - return '', s - - -class DTCAndStatusRecord(Packet): - name = 'DTC and status record' - fields_desc = [ - PacketField("dtc", None, pkt_cls=DTC), FlagsField("status", 0, 8, UDS_RDTCI.dtcStatus) ] @@ -1069,11 +1048,12 @@ class UDS_RDTCIPR(Packet): name = 'ReadDTCInformationPositiveResponse' fields_desc = [ ByteEnumField('reportType', 0, UDS_RDTCI.reportTypes), - ConditionalField( - FlagsField('DTCStatusAvailabilityMask', 0, 8, UDS_RDTCI.dtcStatus), - lambda pkt: pkt.reportType in [0x01, 0x07, 0x11, 0x12, 0x02, 0x0A, - 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x13, - 0x15]), + ConditionalField(FlagsField('DTCStatusAvailabilityMask', 0, 8, UDS_RDTCI.dtcStatus), + lambda pkt: pkt.reportType in [0x01, 0x07, 0x11, + 0x12, 0x02, 0x0A, + 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x13, + 0x15]), ConditionalField(ByteEnumField('DTCFormatIdentifier', 0, {0: 'ISO15031-6DTCFormat', 1: 'UDS-1DTCFormat', @@ -1084,8 +1064,7 @@ class UDS_RDTCIPR(Packet): ConditionalField(ShortField('DTCCount', 0), lambda pkt: pkt.reportType in [0x01, 0x07, 0x11, 0x12]), - ConditionalField(PacketListField('DTCAndStatusRecord', None, - pkt_cls=DTCAndStatusRecord), + ConditionalField(PacketListField('DTCAndStatusRecord', None, pkt_cls=DTC), lambda pkt: pkt.reportType in [0x02, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x13, 0x15]), @@ -1099,7 +1078,7 @@ class UDS_RDTCIPR(Packet): def answers(self, other): return isinstance(other, UDS_RDTCI) \ - and other.reportType == self.reportType + and other.reportType == self.reportType bind_layers(UDS, UDS_RDTCIPR, service=0x59) @@ -1134,8 +1113,8 @@ class UDS_RCPR(Packet): def answers(self, other): return isinstance(other, UDS_RC) \ - and other.routineControlType == self.routineControlType \ - and other.routineIdentifier == self.routineIdentifier + and other.routineControlType == self.routineControlType \ + and other.routineIdentifier == self.routineIdentifier bind_layers(UDS, UDS_RCPR, service=0x71) @@ -1254,7 +1233,7 @@ class UDS_TDPR(Packet): def answers(self, other): return isinstance(other, UDS_TD) \ - and other.blockSequenceCounter == self.blockSequenceCounter + and other.blockSequenceCounter == self.blockSequenceCounter bind_layers(UDS, UDS_TDPR, service=0x76) @@ -1394,7 +1373,7 @@ class UDS_IOCBIPR(Packet): def answers(self, other): return isinstance(other, UDS_IOCBI) \ - and other.dataIdentifier == self.dataIdentifier + and other.dataIdentifier == self.dataIdentifier bind_layers(UDS, UDS_IOCBIPR, service=0x6F) @@ -1459,8 +1438,8 @@ class UDS_NR(Packet): def answers(self, other): return self.requestServiceId == other.service and \ - (self.negativeResponseCode != 0x78 or - conf.contribs['UDS']['treat-response-pending-as-answer']) + (self.negativeResponseCode != 0x78 or + conf.contribs['UDS']['treat-response-pending-as-answer']) bind_layers(UDS, UDS_NR, service=0x7f) diff --git a/test/contrib/automotive/uds.uts b/test/contrib/automotive/uds.uts index ea3bb085530..c3ce6ea342d 100644 --- a/test/contrib/automotive/uds.uts +++ b/test/contrib/automotive/uds.uts @@ -1047,18 +1047,16 @@ assert rdtcipr.DTCCount == 0xddaa assert rdtcipr.answers(rdtci) rdtcipr1 = UDS(b'\x59\x02\xff\x11\x07\x11\'\x022\x12\'\x01\x07\x11\'\x01\x18\x12\'\x01\x13\x12\'\x01"\x11\'\x06C\x00\'\x06S\x00\'\x161\x00\'\x14\x03\x12\'') - +rdtcipr1.show() assert len(rdtcipr1.DTCAndStatusRecord) == 10 -assert rdtcipr1.DTCAndStatusRecord[0].dtc.system == 0 -assert rdtcipr1.DTCAndStatusRecord[0].dtc.type == 1 -assert rdtcipr1.DTCAndStatusRecord[0].dtc.numeric_value_code == 263 -assert rdtcipr1.DTCAndStatusRecord[0].dtc.additional_information_code == 17 -assert rdtcipr1.DTCAndStatusRecord[0].status == 0x27 -assert rdtcipr1.DTCAndStatusRecord[-1].dtc.system == 0 -assert rdtcipr1.DTCAndStatusRecord[-1].dtc.type == 1 -assert rdtcipr1.DTCAndStatusRecord[-1].dtc.numeric_value_code == 1027 -assert rdtcipr1.DTCAndStatusRecord[-1].dtc.additional_information_code == 18 -assert rdtcipr1.DTCAndStatusRecord[-1].status == 0x27 +assert rdtcipr1.DTCAndStatusRecord[0].system == 0 +assert rdtcipr1.DTCAndStatusRecord[0].type == 1 +assert rdtcipr1.DTCAndStatusRecord[0].numeric_value_code == 263 +assert rdtcipr1.DTCAndStatusRecord[0].additional_information_code == 17 +assert rdtcipr1.DTCAndStatusRecord[-1].system == 0 +assert rdtcipr1.DTCAndStatusRecord[-1].type == 1 +assert rdtcipr1.DTCAndStatusRecord[-1].numeric_value_code == 1027 +assert rdtcipr1.DTCAndStatusRecord[-1].additional_information_code == 18 = Check UDS_RDTCI @@ -1175,11 +1173,10 @@ rdtcipr.show() assert rdtcipr.service == 0x59 assert rdtcipr.reportType == 2 assert rdtcipr.DTCStatusAvailabilityMask == 0xff -assert rdtcipr.DTCAndStatusRecord[0].dtc.system == 3 -assert rdtcipr.DTCAndStatusRecord[0].dtc.type == 2 -assert rdtcipr.DTCAndStatusRecord[0].dtc.numeric_value_code == 3805 -assert rdtcipr.DTCAndStatusRecord[0].dtc.additional_information_code == 170 -assert rdtcipr.DTCAndStatusRecord[0].status == 2 +assert rdtcipr.DTCAndStatusRecord[0].system == 3 +assert rdtcipr.DTCAndStatusRecord[0].type == 2 +assert rdtcipr.DTCAndStatusRecord[0].numeric_value_code == 3805 +assert rdtcipr.DTCAndStatusRecord[0].additional_information_code == 170 assert not rdtcipr.answers(rdtci) From 896e7ce176e0650006809d0e469586d72fba4213 Mon Sep 17 00:00:00 2001 From: Nils Weiss Date: Mon, 11 Sep 2023 08:07:57 +0200 Subject: [PATCH 2/9] Add some more docstrings --- scapy/contrib/automotive/uds.py | 52 ++++++++++++++------------- scapy/supersocket.py | 62 +++++++++++++++++++++++++++++++-- 2 files changed, 86 insertions(+), 28 deletions(-) diff --git a/scapy/contrib/automotive/uds.py b/scapy/contrib/automotive/uds.py index 0bd2343288d..b87afeca551 100644 --- a/scapy/contrib/automotive/uds.py +++ b/scapy/contrib/automotive/uds.py @@ -152,7 +152,7 @@ class UDS_DSCPR(Packet): def answers(self, other): return isinstance(other, UDS_DSC) and \ - other.diagnosticSessionType == self.diagnosticSessionType + other.diagnosticSessionType == self.diagnosticSessionType bind_layers(UDS, UDS_DSCPR, service=0x50) @@ -218,7 +218,7 @@ class UDS_SAPR(Packet): def answers(self, other): return isinstance(other, UDS_SA) \ - and other.securityAccessType == self.securityAccessType + and other.securityAccessType == self.securityAccessType bind_layers(UDS, UDS_SAPR, service=0x67) @@ -273,7 +273,7 @@ class UDS_CCPR(Packet): def answers(self, other): return isinstance(other, UDS_CC) \ - and other.controlType == self.controlType + and other.controlType == self.controlType bind_layers(UDS, UDS_CCPR, service=0x68) @@ -429,7 +429,7 @@ class UDS_AUTHPR(Packet): def answers(self, other): return isinstance(other, UDS_AUTH) \ - and other.subFunction == self.subFunction + and other.subFunction == self.subFunction bind_layers(UDS, UDS_AUTHPR, service=0x69) @@ -491,8 +491,7 @@ class UDS_ATPPR(Packet): def answers(self, other): return isinstance(other, UDS_ATP) \ - and other.timingParameterAccessType == \ - self.timingParameterAccessType + and other.timingParameterAccessType == self.timingParameterAccessType bind_layers(UDS, UDS_ATPPR, service=0xC3) @@ -605,7 +604,7 @@ class UDS_ROEPR(Packet): def answers(self, other): return isinstance(other, UDS_ROE) \ - and other.eventType == self.eventType + and other.eventType == self.eventType bind_layers(UDS, UDS_ROEPR, service=0xC6) @@ -644,7 +643,7 @@ class UDS_LCPR(Packet): def answers(self, other): return isinstance(other, UDS_LC) \ - and other.linkControlType == self.linkControlType + and other.linkControlType == self.linkControlType bind_layers(UDS, UDS_LCPR, service=0xC7) @@ -673,7 +672,7 @@ class UDS_RDBIPR(Packet): def answers(self, other): return isinstance(other, UDS_RDBI) \ - and self.dataIdentifier in other.identifiers + and self.dataIdentifier in other.identifiers bind_layers(UDS, UDS_RDBIPR, service=0x62) @@ -743,7 +742,7 @@ class UDS_RSDBIPR(Packet): def answers(self, other): return isinstance(other, UDS_RSDBI) \ - and other.dataIdentifier == self.dataIdentifier + and other.dataIdentifier == self.dataIdentifier bind_layers(UDS, UDS_RSDBIPR, service=0x64) @@ -780,7 +779,7 @@ class UDS_RDBPIPR(Packet): def answers(self, other): return isinstance(other, UDS_RDBPI) \ - and other.periodicDataIdentifier == self.periodicDataIdentifier + and other.periodicDataIdentifier == self.periodicDataIdentifier bind_layers(UDS, UDS_RDBPIPR, service=0x6A) @@ -812,7 +811,7 @@ class UDS_DDDIPR(Packet): def answers(self, other): return isinstance(other, UDS_DDDI) \ - and other.subFunction == self.subFunction + and other.subFunction == self.subFunction bind_layers(UDS, UDS_DDDIPR, service=0x6C) @@ -839,7 +838,7 @@ class UDS_WDBIPR(Packet): def answers(self, other): return isinstance(other, UDS_WDBI) \ - and other.dataIdentifier == self.dataIdentifier + and other.dataIdentifier == self.dataIdentifier bind_layers(UDS, UDS_WDBIPR, service=0x6E) @@ -900,8 +899,8 @@ class UDS_WMBAPR(Packet): def answers(self, other): return isinstance(other, UDS_WMBA) \ - and other.memorySizeLen == self.memorySizeLen \ - and other.memoryAddressLen == self.memoryAddressLen + and other.memorySizeLen == self.memorySizeLen \ + and other.memoryAddressLen == self.memoryAddressLen bind_layers(UDS, UDS_WMBAPR, service=0x7D) @@ -1013,8 +1012,10 @@ class UDS_RDTCI(Packet): class DTC(Packet): name = 'DTC and status record' fields_desc = [ - BitEnumField("system", 0, 2, {0: "Powertrain", 1: "Chassis", 2: "Body", 3: "Network"}), - BitEnumField("type", 0, 2, {0: "Generic", 1: "ManufacturerSpecific", 2: "Generic", 3: "Generic"}), + BitEnumField("system", 0, 2, { + 0: "Powertrain", 1: "Chassis", 2: "Body", 3: "Network"}), + BitEnumField("type", 0, 2, { + 0: "Generic", 1: "ManufacturerSpecific", 2: "Generic", 3: "Generic"}), BitField("numeric_value_code", 0, 12), ByteField("additional_information_code", 0), FlagsField("status", 0, 8, UDS_RDTCI.dtcStatus) @@ -1048,7 +1049,8 @@ class UDS_RDTCIPR(Packet): name = 'ReadDTCInformationPositiveResponse' fields_desc = [ ByteEnumField('reportType', 0, UDS_RDTCI.reportTypes), - ConditionalField(FlagsField('DTCStatusAvailabilityMask', 0, 8, UDS_RDTCI.dtcStatus), + ConditionalField(FlagsField('DTCStatusAvailabilityMask', 0, 8, + UDS_RDTCI.dtcStatus), lambda pkt: pkt.reportType in [0x01, 0x07, 0x11, 0x12, 0x02, 0x0A, 0x0B, 0x0C, 0x0D, @@ -1078,7 +1080,7 @@ class UDS_RDTCIPR(Packet): def answers(self, other): return isinstance(other, UDS_RDTCI) \ - and other.reportType == self.reportType + and other.reportType == self.reportType bind_layers(UDS, UDS_RDTCIPR, service=0x59) @@ -1113,8 +1115,8 @@ class UDS_RCPR(Packet): def answers(self, other): return isinstance(other, UDS_RC) \ - and other.routineControlType == self.routineControlType \ - and other.routineIdentifier == self.routineIdentifier + and other.routineControlType == self.routineControlType \ + and other.routineIdentifier == self.routineIdentifier bind_layers(UDS, UDS_RCPR, service=0x71) @@ -1233,7 +1235,7 @@ class UDS_TDPR(Packet): def answers(self, other): return isinstance(other, UDS_TD) \ - and other.blockSequenceCounter == self.blockSequenceCounter + and other.blockSequenceCounter == self.blockSequenceCounter bind_layers(UDS, UDS_TDPR, service=0x76) @@ -1373,7 +1375,7 @@ class UDS_IOCBIPR(Packet): def answers(self, other): return isinstance(other, UDS_IOCBI) \ - and other.dataIdentifier == self.dataIdentifier + and other.dataIdentifier == self.dataIdentifier bind_layers(UDS, UDS_IOCBIPR, service=0x6F) @@ -1438,8 +1440,8 @@ class UDS_NR(Packet): def answers(self, other): return self.requestServiceId == other.service and \ - (self.negativeResponseCode != 0x78 or - conf.contribs['UDS']['treat-response-pending-as-answer']) + (self.negativeResponseCode != 0x78 or + conf.contribs['UDS']['treat-response-pending-as-answer']) bind_layers(UDS, UDS_NR, service=0x7f) diff --git a/scapy/supersocket.py b/scapy/supersocket.py index 0a05749f214..f1474d7a8bd 100644 --- a/scapy/supersocket.py +++ b/scapy/supersocket.py @@ -102,6 +102,13 @@ def __init__(self, def send(self, x): # type: (Packet) -> int + """Sends a `Packet` object + + :param x: `Packet` to be send + :type x: Packet + :return: Number of bytes that have been sent + :rtype: int + """ sx = raw(x) try: x.sent_time = time.time() @@ -116,7 +123,15 @@ def send(self, x): if WINDOWS: def _recv_raw(self, sock, x): # type: (socket.socket, int) -> Tuple[bytes, Any, Optional[float]] - """Internal function to receive a Packet""" + """Internal function to receive a Packet. + + :param sock: Socket object from which data are received + :type sock: socket.socket + :param x: Number of bytes to be received + :type x: int + :return: Received bytes, address information and no timestamp + :rtype: Tuple[bytes, Any, Optional[float]] + """ pkt, sa_ll = sock.recvfrom(x) return pkt, sa_ll, None else: @@ -124,6 +139,13 @@ def _recv_raw(self, sock, x): # type: (socket.socket, int) -> Tuple[bytes, Any, Optional[float]] """Internal function to receive a Packet, and process ancillary data. + + :param sock: Socket object from which data are received + :type sock: socket.socket + :param x: Number of bytes to be received + :type x: int + :return: Received bytes, address information and an optional timestamp + :rtype: Tuple[bytes, Any, Optional[float]] """ timestamp = None if not self.auxdata_available: @@ -172,11 +194,26 @@ def _recv_raw(self, sock, x): def recv_raw(self, x=MTU): # type: (int) -> Tuple[Optional[Type[Packet]], Optional[bytes], Optional[float]] # noqa: E501 - """Returns a tuple containing (cls, pkt_data, time)""" + """Returns a tuple containing (cls, pkt_data, time) + + + :param x: Maximum number of bytes to be received, defaults to MTU + :type x: int, optional + :return: A tuple, consisting of a Packet type, the received data, + and a timestamp + :rtype: Tuple[Optional[Type[Packet]], Optional[bytes], Optional[float]] + """ return conf.raw_layer, self.ins.recv(x), None - def recv(self, x=MTU, **kwargs): +def recv(self, x=MTU, **kwargs): # type: (int, **Any) -> Optional[Packet] + """Receive a Packet according to the `basecls` of this socket + + :param x: Maximum number of bytes to be received, defaults to MTU + :type x: int, optional + :return: The received `Packet` object, or None + :rtype: Optional[Packet] + """ cls, val, ts = self.recv_raw(x) if not val or not cls: return None @@ -200,6 +237,8 @@ def fileno(self): def close(self): # type: () -> None + """Gracefully close this socket + """ if self.closed: return self.closed = True @@ -213,6 +252,23 @@ def close(self): def sr(self, *args, **kargs): # type: (Any, Any) -> Tuple[SndRcvList, PacketList] + """Send and Receive multiple packets + + :param pkt: Packet or list of packets to be sent + :type pkt: _PacketIterable + :param timeout: Time in seconds for how long packets are received + :type timeout: Optional[int] + :param inter: Delay, between send of two packets + :type inter: int + :param verbose: 1, to enable verbose output, 0 to disable. + :type verbose: Optional[int] + :param chainCC: Forward KeyboardInterrupt, if received. + :type chainCC: bool + + :return: A tuple, consisting of two packet lists, one with + answered packets, the other with unanswered packets + :rtype: Tuple[SndRcvList, PacketList] + """ from scapy import sendrecv ans, unans = sendrecv.sndrcv(self, *args, **kargs) return ans, unans From f7c71462ba895a7d32d9e3ab2e0095987fbc967c Mon Sep 17 00:00:00 2001 From: Nils Weiss Date: Mon, 11 Sep 2023 13:51:55 +0200 Subject: [PATCH 3/9] update some docs revert changes revert changes --- scapy/contrib/automotive/uds.py | 87 ++----------- scapy/sendrecv.py | 221 +++++++++++++++++++++++--------- scapy/supersocket.py | 17 +-- test/contrib/automotive/uds.uts | 20 +-- 4 files changed, 173 insertions(+), 172 deletions(-) diff --git a/scapy/contrib/automotive/uds.py b/scapy/contrib/automotive/uds.py index b87afeca551..be2af943fb0 100644 --- a/scapy/contrib/automotive/uds.py +++ b/scapy/contrib/automotive/uds.py @@ -16,7 +16,7 @@ BitEnumField, BitField, XByteField, FieldListField, \ XShortField, X3BytesField, XIntField, ByteField, \ ShortField, ObservableDict, XShortEnumField, XByteEnumField, StrLenField, \ - FieldLenField, XStrFixedLenField, XStrLenField, FlagsField, PacketListField + FieldLenField, XStrFixedLenField, XStrLenField from scapy.packet import Packet, bind_layers, NoPayload from scapy.config import conf from scapy.error import log_loading @@ -491,7 +491,8 @@ class UDS_ATPPR(Packet): def answers(self, other): return isinstance(other, UDS_ATP) \ - and other.timingParameterAccessType == self.timingParameterAccessType + and other.timingParameterAccessType == \ + self.timingParameterAccessType bind_layers(UDS, UDS_ATPPR, service=0xC3) @@ -955,39 +956,12 @@ class UDS_RDTCI(Packet): 20: 'reportDTCFaultDetectionCounter', 21: 'reportDTCWithPermanentStatus' } - dtcStatus = { - 1: 'TestFailed', - 2: 'TestFailedThisOperationCycle', - 4: 'PendingDTC', - 8: 'ConfirmedDTC', - 16: 'TestNotCompletedSinceLastClear', - 32: 'TestFailedSinceLastClear', - 64: 'TestNotCompletedThisOperationCycle', - 128: 'WarningIndicatorRequested' - } - dtcStatusMask = { - 1: 'ActiveDTCs', - 4: 'PendingDTCs', - 8: 'ConfirmedOrStoredDTCs', - 255: 'AllRecordDTCs' - } - dtcSeverityMask = { - # 0: 'NoSeverityInformation', - 1: 'NoClassInformation', - 2: 'WWH-OBDClassA', - 4: 'WWH-OBDClassB1', - 8: 'WWH-OBDClassB2', - 16: 'WWH-OBDClassC', - 32: 'MaintenanceRequired', - 64: 'CheckAtNextHalt', - 128: 'CheckImmediately' - } name = 'ReadDTCInformation' fields_desc = [ ByteEnumField('reportType', 0, reportTypes), - ConditionalField(FlagsField('DTCSeverityMask', 0, 8, dtcSeverityMask), + ConditionalField(ByteField('DTCSeverityMask', 0), lambda pkt: pkt.reportType in [0x07, 0x08]), - ConditionalField(FlagsField('DTCStatusMask', 0, 8, dtcStatusMask), + ConditionalField(XByteField('DTCStatusMask', 0), lambda pkt: pkt.reportType in [ 0x01, 0x02, 0x07, 0x08, 0x0f, 0x11, 0x12, 0x13]), ConditionalField(ByteField('DTCHighByte', 0), @@ -1009,48 +983,11 @@ class UDS_RDTCI(Packet): bind_layers(UDS, UDS_RDTCI, service=0x19) -class DTC(Packet): - name = 'DTC and status record' - fields_desc = [ - BitEnumField("system", 0, 2, { - 0: "Powertrain", 1: "Chassis", 2: "Body", 3: "Network"}), - BitEnumField("type", 0, 2, { - 0: "Generic", 1: "ManufacturerSpecific", 2: "Generic", 3: "Generic"}), - BitField("numeric_value_code", 0, 12), - ByteField("additional_information_code", 0), - FlagsField("status", 0, 8, UDS_RDTCI.dtcStatus) - ] - - def extract_padding(self, s): - return '', s - - -class DTCExtendedData(Packet): - name = 'Diagnostic Trouble Code Extended Data' - dataTypes = ObservableDict() - - fields_desc = [ - ByteEnumField("data_type", 0, dataTypes), - XByteField("record", 0) - ] - - def extract_padding(self, s): - return '', s - - -class DTCExtendedDataRecord(Packet): - fields_desc = [ - PacketField("dtcAndStatus", None, pkt_cls=DTCAndStatusRecord), - PacketListField("extendedData", None, pkt_cls=DTCExtendedData) - ] - - class UDS_RDTCIPR(Packet): name = 'ReadDTCInformationPositiveResponse' fields_desc = [ ByteEnumField('reportType', 0, UDS_RDTCI.reportTypes), - ConditionalField(FlagsField('DTCStatusAvailabilityMask', 0, 8, - UDS_RDTCI.dtcStatus), + ConditionalField(XByteField('DTCStatusAvailabilityMask', 0), lambda pkt: pkt.reportType in [0x01, 0x07, 0x11, 0x12, 0x02, 0x0A, 0x0B, 0x0C, 0x0D, @@ -1066,7 +1003,7 @@ class UDS_RDTCIPR(Packet): ConditionalField(ShortField('DTCCount', 0), lambda pkt: pkt.reportType in [0x01, 0x07, 0x11, 0x12]), - ConditionalField(PacketListField('DTCAndStatusRecord', None, pkt_cls=DTC), + ConditionalField(StrField('DTCAndStatusRecord', b""), lambda pkt: pkt.reportType in [0x02, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x13, 0x15]), @@ -1324,15 +1261,15 @@ def _contains_data_format_identifier(packet): fmt='B'), lambda p: p.modeOfOperation != 2), ConditionalField(StrLenField('maxNumberOfBlockLength', b"", - length_from=lambda p: p.lengthFormatIdentifier), + length_from=lambda p: p.lengthFormatIdentifier), lambda p: p.modeOfOperation != 2), ConditionalField(BitField('compressionMethod', 0, 4), lambda p: p.modeOfOperation != 0x02), ConditionalField(BitField('encryptingMethod', 0, 4), lambda p: p.modeOfOperation != 0x02), ConditionalField(FieldLenField('fileSizeOrDirInfoParameterLength', - None, - length_of='fileSizeUncompressedOrDirInfoLength'), + None, + length_of='fileSizeUncompressedOrDirInfoLength'), lambda p: p.modeOfOperation not in [1, 2, 3]), ConditionalField(StrLenField('fileSizeUncompressedOrDirInfoLength', b"", @@ -1340,8 +1277,8 @@ def _contains_data_format_identifier(packet): p.fileSizeOrDirInfoParameterLength), lambda p: p.modeOfOperation not in [1, 2, 3]), ConditionalField(StrLenField('fileSizeCompressed', b"", - length_from=lambda p: - p.fileSizeOrDirInfoParameterLength), + length_from=lambda p: + p.fileSizeOrDirInfoParameterLength), lambda p: p.modeOfOperation not in [1, 2, 3, 5]), ] diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py index c62ecc6901e..5497645d15d 100644 --- a/scapy/sendrecv.py +++ b/scapy/sendrecv.py @@ -74,25 +74,72 @@ class debug: # Send / Receive # #################### -_DOC_SNDRCV_PARAMS = """ +_DOC_SNDRCV_PARAMS_HEAD = """ :param pks: SuperSocket instance to send/receive packets - :param pkt: the packet to send - :param timeout: how much time to wait after the last packet has been sent - :param inter: delay between two packets during sending - :param verbose: set verbosity level - :param chainCC: if True, KeyboardInterrupts will be forwarded - :param retry: if positive, how many times to resend unanswered packets - if negative, how many times to retry when no more packets - are answered - :param multi: whether to accept multiple answers for the same stimulus - :param rcv_pks: if set, will be used instead of pks to receive packets. - packets will still be sent through pks - :param prebuild: pre-build the packets before starting to send them. - Automatically enabled when a generator is passed as the packet - :param _flood: - :param threaded: if True, packets will be sent in an individual thread - :param session: a flow decoder used to handle stream of packets - :param chainEX: if True, exceptions during send will be forwarded + :type pkt: SuperSocket + """ + +_DOC_SNDRCV_PARAMS_BODY = """ + :param pkt: Packet or iterable of packets to be sent. + :type pkt: _PacketIterable + :param timeout: How much time to wait after the last packet + has been sent. Defaults to None. + :type timeout: Optional[int] + :param inter: Delay between two packets during sending. Defaults to 0. + :type inter: Optional[int] + + :param verbose: Set verbosity level. Defaults to None. + :type verbose: Optional[int] + + :param chainCC: If True, KeyboardInterrupts will be forwarded. + Defaults to False. + :type chainCC: Optional[bool] + + :param retry: If positive, how many times to resend unanswered packets. + If negative, how many times to retry when no more packets + are answered. Defaults to 0. + :type retry: Optional[int] + + :param multi: Whether to accept multiple answers for the same stimulus. + Defaults to False. + :type multi: Optional[bool] + + :param rcv_pks: If set, will be used instead of pks to receive packets. + Packets will still be sent through pks. + Defaults to None. + :type rcv_pks: Optional[SuperSocket] + + :param prebuild: Pre-build the packets before starting to send them. + Automatically enabled when a generator is passed as the + packet. Defaults to False. + :type prebuild: Optional[bool] + + :param _flood: _FloodGenerator object, internally used by `flood()` + methods. Defaults to None. + :type _flood: Optional[_FloodGenerator] + + :param threaded: If True, packets will be sent in an individual thread. + Defaults to False. + :type threaded: Optional[bool] + + :param session: A flow decoder used to handle the stream of packets. + Defaults to None. + :type session: Optional[_GlobSessionType] + + :param chainEX: If True, exceptions during send will be forwarded. + Defaults to False. + :type chainEX: Optional[bool] +""" + +_DOC_SNDRCV_PARAMS_TAIL = """ + :return: A tuple, consisting of two packet lists, one with + answered packets, the other with unanswered packets + :rtype: Tuple[SndRcvList, PacketList] + """ + +_DOC_SNDRCV1_PARAMS_TAIL = """ + :return: A received Packet answering the sent packet, or None + :rtype: Optional[Packet] """ @@ -717,9 +764,26 @@ def srp1(*args, **kargs): # Append doc -for sr_func in [srp, srp1, sr, sr1]: +for sr_func in [srp, sr]: if sr_func.__doc__ is not None: - sr_func.__doc__ += _DOC_SNDRCV_PARAMS + sr_func.__doc__ += (_DOC_SNDRCV_PARAMS_HEAD + + _DOC_SNDRCV_PARAMS_BODY + + _DOC_SNDRCV_PARAMS_TAIL) + +for sr_func in [srp1, sr1]: + if sr_func.__doc__ is not None: + sr_func.__doc__ += (_DOC_SNDRCV_PARAMS_HEAD + + _DOC_SNDRCV_PARAMS_BODY + + _DOC_SNDRCV1_PARAMS_TAIL) + +# Append doc in SuperSocket +for sr_func in [SuperSocket.sr]: + if sr_func.__doc__ is not None: + sr_func.__doc__ += _DOC_SNDRCV_PARAMS_BODY + _DOC_SNDRCV_PARAMS_TAIL + +for sr_func in [SuperSocket.sr1]: + if sr_func.__doc__ is not None: + sr_func.__doc__ += _DOC_SNDRCV_PARAMS_BODY + _DOC_SNDRCV1_PARAMS_TAIL # SEND/RECV LOOP METHODS @@ -999,40 +1063,49 @@ def srp1flood(x, # type: _PacketIterable # SNIFF METHODS -class AsyncSniffer(object): - """ - Sniff packets and return a list of packets. - - Args: - count: number of packets to capture. 0 means infinity. - store: whether to store sniffed packets or discard them - prn: function to apply to each packet. If something is returned, it - is displayed. - --Ex: prn = lambda x: x.summary() - session: a session = a flow decoder used to handle stream of packets. - --Ex: session=TCPSession - See below for more details. - filter: BPF filter to apply. - lfilter: Python function applied to each packet to determine if - further action may be done. - --Ex: lfilter = lambda x: x.haslayer(Padding) - offline: PCAP file (or list of PCAP files) to read packets from, - instead of sniffing them - quiet: when set to True, the process stderr is discarded - (default: False). - timeout: stop sniffing after a given time (default: None). - L2socket: use the provided L2socket (default: use conf.L2listen). - opened_socket: provide an object (or a list of objects) ready to use - .recv() on. - stop_filter: Python function applied to each packet to determine if - we have to stop the capture after this packet. - --Ex: stop_filter = lambda x: x.haslayer(TCP) - iface: interface or list of interfaces (default: None for sniffing - on all interfaces). - monitor: use monitor mode. May not be available on all OS - started_callback: called as soon as the sniffer starts sniffing - (default: None). +_DOC_SNIFF_PARAMS = """ + :param count: Number of packets to capture. 0 means infinity. + :type count: int + :param store: Whether to store sniffed packets or discard them. + :type store: bool + :param offline: PCAP file (or list of PCAP files) to read packets from, + instead of sniffing them. + :type offline: Any + :param quiet: When set to True, the process stderr is discarded. + (default: False). + :type quiet: bool + :param prn: Function to apply to each packet. If something is returned, + it is displayed. + --Ex: prn = lambda x: x.summary() + :type prn: Optional[Callable[[Packet], Any]] + :param lfilter: Python function applied to each packet to determine if + further action may be done. + :type lfilter: Optional[Callable[[Packet], bool]] + :param L2socket: Use the provided L2socket (default: use conf.L2listen). + :type L2socket: Optional[Type[SuperSocket]] + :param timeout: Stop sniffing after a given time (default: None). + :type timeout: Optional[int] + :param opened_socket: Provide an object (or a list of objects) ready to + use .recv() on. + :type opened_socket: Optional[SuperSocket] + :param stop_filter: Python function applied to each packet to determine if + we have to stop the capture after this packet. + :type stop_filter: Optional[Callable[[Packet], bool]] + :param iface: Interface or list of interfaces (default: None for sniffing + on all interfaces). + :type iface: Optional[_GlobInterfaceType] + :param started_callback: Called as soon as the sniffer starts sniffing + (default: None). + :type started_callback: Optional[Callable[[], Any]] + :param session: A session, which is a flow decoder used to handle a stream + of packets. See the documentation for more details. + :type session: Optional[_GlobSessionType] + :param session_kwargs: Additional keyword arguments for session initialization. + :type session_kwargs: Dict[str, Any] + +""" +_DOC_ASYNC_SNIFF = """ The iface, offline and opened_socket parameters can be either an element, a list of elements, or a dict object mapping an element to a label (see examples below). @@ -1062,6 +1135,11 @@ class AsyncSniffer(object): >>> t.stop() """ + +class AsyncSniffer(object): + """Sniff packets and return a list of packets. + """ + def __init__(self, *args, **kwargs): # type: (*Any, **Any) -> None # Store keyword arguments @@ -1330,6 +1408,10 @@ def join(self, *args, **kwargs): self.thread.join(*args, **kwargs) +AsyncSniffer.__doc__ = ((AsyncSniffer.__doc__ or "") + _DOC_SNIFF_PARAMS + + _DOC_ASYNC_SNIFF) + + @conf.commands.register def sniff(*args, **kwargs): # type: (*Any, **Any) -> PacketList @@ -1338,7 +1420,7 @@ def sniff(*args, **kwargs): return cast(PacketList, sniffer.results) -sniff.__doc__ = AsyncSniffer.__doc__ +SuperSocket.sniff.__doc__ = sniff.__doc__ = AsyncSniffer.__doc__ @conf.commands.register @@ -1355,18 +1437,21 @@ def bridge_and_sniff(if1, # type: _GlobInterfaceType """Forward traffic between interfaces if1 and if2, sniff and return the exchanged packets. - :param if1: the interfaces to use (interface names or opened sockets). - :param if2: - :param xfrm12: a function to call when forwarding a packet from if1 to + :param if1: The interfaces to use (interface names or opened sockets). + :type if1: _GlobInterfaceType + + :param if2: The interfaces to use (interface names or opened sockets). + :type if2: _GlobInterfaceType + + :param xfrm12: A function to call when forwarding a packet from if1 to if2. If it returns True, the packet is forwarded as it. If it returns False or None, the packet is discarded. If it returns a - packet, this packet is forwarded instead of the original packet - one. - :param xfrm21: same as xfrm12 for packets forwarded from if2 to if1. + packet, this packet is forwarded instead of the original packet. + :type xfrm12: Optional[Callable[[Packet], Union[Packet, bool]]] + + :param xfrm21: Same as xfrm12 for packets forwarded from if2 to if1. + :type xfrm21: Optional[Callable[[Packet], Union[Packet, bool]]] - The other arguments are the same than for the function sniff(), - except for offline, opened_socket and iface that are ignored. - See help(sniff) for more. """ for arg in ['opened_socket', 'offline', 'iface']: if arg in kargs: @@ -1439,11 +1524,15 @@ def prn(pkt): *args, **kargs) +bridge_and_sniff.__doc__ = (bridge_and_sniff.__doc__ or "") + _DOC_SNIFF_PARAMS + + @conf.commands.register def tshark(*args, **kargs): # type: (Any, Any) -> None """Sniff packets and print them calling pkt.summary(). - This tries to replicate what text-wireshark (tshark) would look like""" + This tries to replicate what text-wireshark (tshark) would look like. + """ if 'iface' in kargs: iface = kargs.get('iface') @@ -1464,3 +1553,7 @@ def _cb(pkt): sniff(prn=_cb, store=False, *args, **kargs) print("\n%d packet%s captured" % (i[0], 's' if i[0] > 1 else '')) + + +tshark.__doc__ = (tshark.__doc__ or "") + _DOC_SNIFF_PARAMS +SuperSocket.tshark.__doc__ = tshark.__doc__ diff --git a/scapy/supersocket.py b/scapy/supersocket.py index f1474d7a8bd..f4797727ddc 100644 --- a/scapy/supersocket.py +++ b/scapy/supersocket.py @@ -253,21 +253,6 @@ def close(self): def sr(self, *args, **kargs): # type: (Any, Any) -> Tuple[SndRcvList, PacketList] """Send and Receive multiple packets - - :param pkt: Packet or list of packets to be sent - :type pkt: _PacketIterable - :param timeout: Time in seconds for how long packets are received - :type timeout: Optional[int] - :param inter: Delay, between send of two packets - :type inter: int - :param verbose: 1, to enable verbose output, 0 to disable. - :type verbose: Optional[int] - :param chainCC: Forward KeyboardInterrupt, if received. - :type chainCC: bool - - :return: A tuple, consisting of two packet lists, one with - answered packets, the other with unanswered packets - :rtype: Tuple[SndRcvList, PacketList] """ from scapy import sendrecv ans, unans = sendrecv.sndrcv(self, *args, **kargs) @@ -275,6 +260,8 @@ def sr(self, *args, **kargs): def sr1(self, *args, **kargs): # type: (Any, Any) -> Optional[Packet] + """Send one packet and receive one answer + """ from scapy import sendrecv ans = sendrecv.sndrcv(self, *args, **kargs)[0] # type: SndRcvList if len(ans) > 0: diff --git a/test/contrib/automotive/uds.uts b/test/contrib/automotive/uds.uts index c3ce6ea342d..ead0097accb 100644 --- a/test/contrib/automotive/uds.uts +++ b/test/contrib/automotive/uds.uts @@ -1046,18 +1046,6 @@ assert rdtcipr.DTCCount == 0xddaa assert rdtcipr.answers(rdtci) -rdtcipr1 = UDS(b'\x59\x02\xff\x11\x07\x11\'\x022\x12\'\x01\x07\x11\'\x01\x18\x12\'\x01\x13\x12\'\x01"\x11\'\x06C\x00\'\x06S\x00\'\x161\x00\'\x14\x03\x12\'') -rdtcipr1.show() -assert len(rdtcipr1.DTCAndStatusRecord) == 10 -assert rdtcipr1.DTCAndStatusRecord[0].system == 0 -assert rdtcipr1.DTCAndStatusRecord[0].type == 1 -assert rdtcipr1.DTCAndStatusRecord[0].numeric_value_code == 263 -assert rdtcipr1.DTCAndStatusRecord[0].additional_information_code == 17 -assert rdtcipr1.DTCAndStatusRecord[-1].system == 0 -assert rdtcipr1.DTCAndStatusRecord[-1].type == 1 -assert rdtcipr1.DTCAndStatusRecord[-1].numeric_value_code == 1027 -assert rdtcipr1.DTCAndStatusRecord[-1].additional_information_code == 18 - = Check UDS_RDTCI rdtci = UDS(b'\x19\x02\xff') @@ -1168,15 +1156,11 @@ assert rdtci.DTCExtendedDataRecordNumber == 0xaa = Check UDS_RDTCIPR -rdtcipr = UDS(b'\x59\x02\xff\xee\xdd\xaa\x02') -rdtcipr.show() +rdtcipr = UDS(b'\x59\x02\xff\xee\xdd\xaa') assert rdtcipr.service == 0x59 assert rdtcipr.reportType == 2 assert rdtcipr.DTCStatusAvailabilityMask == 0xff -assert rdtcipr.DTCAndStatusRecord[0].system == 3 -assert rdtcipr.DTCAndStatusRecord[0].type == 2 -assert rdtcipr.DTCAndStatusRecord[0].numeric_value_code == 3805 -assert rdtcipr.DTCAndStatusRecord[0].additional_information_code == 170 +assert rdtcipr.DTCAndStatusRecord == b'\xee\xdd\xaa' assert not rdtcipr.answers(rdtci) From 6c35df61008b51f093746aa03f00a596c2cb16ca Mon Sep 17 00:00:00 2001 From: Nils Weiss Date: Wed, 11 Oct 2023 12:06:56 +0200 Subject: [PATCH 4/9] revert changes --- scapy/contrib/automotive/uds.py | 90 +++++++++++++++++++++++++++------ test/contrib/automotive/uds.uts | 23 ++++++++- 2 files changed, 96 insertions(+), 17 deletions(-) diff --git a/scapy/contrib/automotive/uds.py b/scapy/contrib/automotive/uds.py index be2af943fb0..c388e0f321b 100644 --- a/scapy/contrib/automotive/uds.py +++ b/scapy/contrib/automotive/uds.py @@ -16,7 +16,8 @@ BitEnumField, BitField, XByteField, FieldListField, \ XShortField, X3BytesField, XIntField, ByteField, \ ShortField, ObservableDict, XShortEnumField, XByteEnumField, StrLenField, \ - FieldLenField, XStrFixedLenField, XStrLenField + FieldLenField, XStrFixedLenField, XStrLenField, FlagsField, PacketListField, \ + PacketField from scapy.packet import Packet, bind_layers, NoPayload from scapy.config import conf from scapy.error import log_loading @@ -956,12 +957,39 @@ class UDS_RDTCI(Packet): 20: 'reportDTCFaultDetectionCounter', 21: 'reportDTCWithPermanentStatus' } + dtcStatus = { + 1: 'TestFailed', + 2: 'TestFailedThisOperationCycle', + 4: 'PendingDTC', + 8: 'ConfirmedDTC', + 16: 'TestNotCompletedSinceLastClear', + 32: 'TestFailedSinceLastClear', + 64: 'TestNotCompletedThisOperationCycle', + 128: 'WarningIndicatorRequested' + } + dtcStatusMask = { + 1: 'ActiveDTCs', + 4: 'PendingDTCs', + 8: 'ConfirmedOrStoredDTCs', + 255: 'AllRecordDTCs' + } + dtcSeverityMask = { + # 0: 'NoSeverityInformation', + 1: 'NoClassInformation', + 2: 'WWH-OBDClassA', + 4: 'WWH-OBDClassB1', + 8: 'WWH-OBDClassB2', + 16: 'WWH-OBDClassC', + 32: 'MaintenanceRequired', + 64: 'CheckAtNextHalt', + 128: 'CheckImmediately' + } name = 'ReadDTCInformation' fields_desc = [ ByteEnumField('reportType', 0, reportTypes), - ConditionalField(ByteField('DTCSeverityMask', 0), + ConditionalField(FlagsField('DTCSeverityMask', 0, 8, dtcSeverityMask), lambda pkt: pkt.reportType in [0x07, 0x08]), - ConditionalField(XByteField('DTCStatusMask', 0), + ConditionalField(FlagsField('DTCStatusMask', 0, 8, dtcStatusMask), lambda pkt: pkt.reportType in [ 0x01, 0x02, 0x07, 0x08, 0x0f, 0x11, 0x12, 0x13]), ConditionalField(ByteField('DTCHighByte', 0), @@ -983,16 +1011,47 @@ class UDS_RDTCI(Packet): bind_layers(UDS, UDS_RDTCI, service=0x19) +class DTC(Packet): + name = 'Diagnostic Trouble Code' + fields_desc = [ + BitEnumField("system", 0, 2, { + 0: "Powertrain", + 1: "Chassis", + 2: "Body", + 3: "Network"}), + BitEnumField("type", 0, 2, { + 0: "Generic", + 1: "ManufacturerSpecific", + 2: "Generic", + 3: "Generic"}), + BitField("numeric_value_code", 0, 12), + ByteField("additional_information_code", 0), + ] + + def extract_padding(self, s): + return '', s + + +class DTC_Status(Packet): + name = 'DTC and status record' + fields_desc = [ + PacketField("dtc", None, pkt_cls=DTC), + FlagsField("status", 0, 8, UDS_RDTCI.dtcStatus) + ] + + def extract_padding(self, s): + return '', s + + class UDS_RDTCIPR(Packet): name = 'ReadDTCInformationPositiveResponse' fields_desc = [ ByteEnumField('reportType', 0, UDS_RDTCI.reportTypes), - ConditionalField(XByteField('DTCStatusAvailabilityMask', 0), - lambda pkt: pkt.reportType in [0x01, 0x07, 0x11, - 0x12, 0x02, 0x0A, - 0x0B, 0x0C, 0x0D, - 0x0E, 0x0F, 0x13, - 0x15]), + ConditionalField( + FlagsField('DTCStatusAvailabilityMask', 0, 8, UDS_RDTCI.dtcStatus), + lambda pkt: pkt.reportType in [0x01, 0x07, 0x11, 0x12, 0x02, 0x0A, + 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x13, + 0x15]), ConditionalField(ByteEnumField('DTCFormatIdentifier', 0, {0: 'ISO15031-6DTCFormat', 1: 'UDS-1DTCFormat', @@ -1003,7 +1062,8 @@ class UDS_RDTCIPR(Packet): ConditionalField(ShortField('DTCCount', 0), lambda pkt: pkt.reportType in [0x01, 0x07, 0x11, 0x12]), - ConditionalField(StrField('DTCAndStatusRecord', b""), + ConditionalField(PacketListField('DTCAndStatusRecord', None, + pkt_cls=DTC_Status), lambda pkt: pkt.reportType in [0x02, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x13, 0x15]), @@ -1261,15 +1321,15 @@ def _contains_data_format_identifier(packet): fmt='B'), lambda p: p.modeOfOperation != 2), ConditionalField(StrLenField('maxNumberOfBlockLength', b"", - length_from=lambda p: p.lengthFormatIdentifier), + length_from=lambda p: p.lengthFormatIdentifier), lambda p: p.modeOfOperation != 2), ConditionalField(BitField('compressionMethod', 0, 4), lambda p: p.modeOfOperation != 0x02), ConditionalField(BitField('encryptingMethod', 0, 4), lambda p: p.modeOfOperation != 0x02), ConditionalField(FieldLenField('fileSizeOrDirInfoParameterLength', - None, - length_of='fileSizeUncompressedOrDirInfoLength'), + None, + length_of='fileSizeUncompressedOrDirInfoLength'), lambda p: p.modeOfOperation not in [1, 2, 3]), ConditionalField(StrLenField('fileSizeUncompressedOrDirInfoLength', b"", @@ -1277,8 +1337,8 @@ def _contains_data_format_identifier(packet): p.fileSizeOrDirInfoParameterLength), lambda p: p.modeOfOperation not in [1, 2, 3]), ConditionalField(StrLenField('fileSizeCompressed', b"", - length_from=lambda p: - p.fileSizeOrDirInfoParameterLength), + length_from=lambda p: + p.fileSizeOrDirInfoParameterLength), lambda p: p.modeOfOperation not in [1, 2, 3, 5]), ] diff --git a/test/contrib/automotive/uds.uts b/test/contrib/automotive/uds.uts index ead0097accb..ea3bb085530 100644 --- a/test/contrib/automotive/uds.uts +++ b/test/contrib/automotive/uds.uts @@ -1046,6 +1046,20 @@ assert rdtcipr.DTCCount == 0xddaa assert rdtcipr.answers(rdtci) +rdtcipr1 = UDS(b'\x59\x02\xff\x11\x07\x11\'\x022\x12\'\x01\x07\x11\'\x01\x18\x12\'\x01\x13\x12\'\x01"\x11\'\x06C\x00\'\x06S\x00\'\x161\x00\'\x14\x03\x12\'') + +assert len(rdtcipr1.DTCAndStatusRecord) == 10 +assert rdtcipr1.DTCAndStatusRecord[0].dtc.system == 0 +assert rdtcipr1.DTCAndStatusRecord[0].dtc.type == 1 +assert rdtcipr1.DTCAndStatusRecord[0].dtc.numeric_value_code == 263 +assert rdtcipr1.DTCAndStatusRecord[0].dtc.additional_information_code == 17 +assert rdtcipr1.DTCAndStatusRecord[0].status == 0x27 +assert rdtcipr1.DTCAndStatusRecord[-1].dtc.system == 0 +assert rdtcipr1.DTCAndStatusRecord[-1].dtc.type == 1 +assert rdtcipr1.DTCAndStatusRecord[-1].dtc.numeric_value_code == 1027 +assert rdtcipr1.DTCAndStatusRecord[-1].dtc.additional_information_code == 18 +assert rdtcipr1.DTCAndStatusRecord[-1].status == 0x27 + = Check UDS_RDTCI rdtci = UDS(b'\x19\x02\xff') @@ -1156,11 +1170,16 @@ assert rdtci.DTCExtendedDataRecordNumber == 0xaa = Check UDS_RDTCIPR -rdtcipr = UDS(b'\x59\x02\xff\xee\xdd\xaa') +rdtcipr = UDS(b'\x59\x02\xff\xee\xdd\xaa\x02') +rdtcipr.show() assert rdtcipr.service == 0x59 assert rdtcipr.reportType == 2 assert rdtcipr.DTCStatusAvailabilityMask == 0xff -assert rdtcipr.DTCAndStatusRecord == b'\xee\xdd\xaa' +assert rdtcipr.DTCAndStatusRecord[0].dtc.system == 3 +assert rdtcipr.DTCAndStatusRecord[0].dtc.type == 2 +assert rdtcipr.DTCAndStatusRecord[0].dtc.numeric_value_code == 3805 +assert rdtcipr.DTCAndStatusRecord[0].dtc.additional_information_code == 170 +assert rdtcipr.DTCAndStatusRecord[0].status == 2 assert not rdtcipr.answers(rdtci) From 6928560ba9f72cfc3efa723ac9db9f9b7b062b73 Mon Sep 17 00:00:00 2001 From: Nils Weiss Date: Wed, 11 Oct 2023 12:09:32 +0200 Subject: [PATCH 5/9] copy past bug fix --- scapy/supersocket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scapy/supersocket.py b/scapy/supersocket.py index f4797727ddc..32a1a1f7be9 100644 --- a/scapy/supersocket.py +++ b/scapy/supersocket.py @@ -205,7 +205,7 @@ def recv_raw(self, x=MTU): """ return conf.raw_layer, self.ins.recv(x), None -def recv(self, x=MTU, **kwargs): + def recv(self, x=MTU, **kwargs): # type: (int, **Any) -> Optional[Packet] """Receive a Packet according to the `basecls` of this socket From 864fe653d663a05162a5779920ec3dc754408735 Mon Sep 17 00:00:00 2001 From: Nils Weiss Date: Thu, 12 Oct 2023 09:52:23 +0200 Subject: [PATCH 6/9] apply feedback --- scapy/sendrecv.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py index 5497645d15d..fdc44c54ab3 100644 --- a/scapy/sendrecv.py +++ b/scapy/sendrecv.py @@ -1408,8 +1408,7 @@ def join(self, *args, **kwargs): self.thread.join(*args, **kwargs) -AsyncSniffer.__doc__ = ((AsyncSniffer.__doc__ or "") + _DOC_SNIFF_PARAMS + - _DOC_ASYNC_SNIFF) +AsyncSniffer.__doc__ = AsyncSniffer.__doc__ + _DOC_SNIFF_PARAMS + _DOC_ASYNC_SNIFF # type: ignore # noqa: E501 @conf.commands.register @@ -1524,7 +1523,7 @@ def prn(pkt): *args, **kargs) -bridge_and_sniff.__doc__ = (bridge_and_sniff.__doc__ or "") + _DOC_SNIFF_PARAMS +bridge_and_sniff.__doc__ = bridge_and_sniff.__doc__ + _DOC_SNIFF_PARAMS # type: ignore # noqa: E501 @conf.commands.register @@ -1555,5 +1554,5 @@ def _cb(pkt): print("\n%d packet%s captured" % (i[0], 's' if i[0] > 1 else '')) -tshark.__doc__ = (tshark.__doc__ or "") + _DOC_SNIFF_PARAMS +tshark.__doc__ = tshark.__doc__ + _DOC_SNIFF_PARAMS # type: ignore # noqa: E501 SuperSocket.tshark.__doc__ = tshark.__doc__ From 1bc0e2a4f0617fc0fba122e244744c6e1e354c2c Mon Sep 17 00:00:00 2001 From: Nils Weiss Date: Wed, 1 Nov 2023 14:21:30 +0100 Subject: [PATCH 7/9] update docs --- scapy/sendrecv.py | 236 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 176 insertions(+), 60 deletions(-) diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py index fdc44c54ab3..2e8be40d281 100644 --- a/scapy/sendrecv.py +++ b/scapy/sendrecv.py @@ -74,75 +74,50 @@ class debug: # Send / Receive # #################### -_DOC_SNDRCV_PARAMS_HEAD = """ - :param pks: SuperSocket instance to send/receive packets - :type pkt: SuperSocket - """ - _DOC_SNDRCV_PARAMS_BODY = """ :param pkt: Packet or iterable of packets to be sent. - :type pkt: _PacketIterable :param timeout: How much time to wait after the last packet has been sent. Defaults to None. - :type timeout: Optional[int] :param inter: Delay between two packets during sending. Defaults to 0. - :type inter: Optional[int] - :param verbose: Set verbosity level. Defaults to None. - :type verbose: Optional[int] - :param chainCC: If True, KeyboardInterrupts will be forwarded. Defaults to False. - :type chainCC: Optional[bool] - :param retry: If positive, how many times to resend unanswered packets. If negative, how many times to retry when no more packets are answered. Defaults to 0. - :type retry: Optional[int] - :param multi: Whether to accept multiple answers for the same stimulus. Defaults to False. - :type multi: Optional[bool] - :param rcv_pks: If set, will be used instead of pks to receive packets. Packets will still be sent through pks. Defaults to None. - :type rcv_pks: Optional[SuperSocket] - :param prebuild: Pre-build the packets before starting to send them. Automatically enabled when a generator is passed as the packet. Defaults to False. - :type prebuild: Optional[bool] - :param _flood: _FloodGenerator object, internally used by `flood()` methods. Defaults to None. - :type _flood: Optional[_FloodGenerator] - :param threaded: If True, packets will be sent in an individual thread. Defaults to False. - :type threaded: Optional[bool] - :param session: A flow decoder used to handle the stream of packets. Defaults to None. - :type session: Optional[_GlobSessionType] - :param chainEX: If True, exceptions during send will be forwarded. Defaults to False. - :type chainEX: Optional[bool] """ _DOC_SNDRCV_PARAMS_TAIL = """ :return: A tuple, consisting of two packet lists, one with - answered packets, the other with unanswered packets - :rtype: Tuple[SndRcvList, PacketList] + answered packets, the other with unanswered packets """ _DOC_SNDRCV1_PARAMS_TAIL = """ :return: A received Packet answering the sent packet, or None - :rtype: Optional[Packet] """ +# Append doc in SuperSocket +SuperSocket.sr.__doc__ += _DOC_SNDRCV_PARAMS_BODY + _DOC_SNDRCV_PARAMS_TAIL # type: ignore +SuperSocket.sr1.__doc__ += _DOC_SNDRCV_PARAMS_BODY + _DOC_SNDRCV1_PARAMS_TAIL # type: ignore + + _GlobSessionType = Union[Type[DefaultSession], DefaultSession] @@ -159,6 +134,34 @@ class SndRcvHandler(object): when sending a big amount of packets. Disabled by default - DEVS: store the outgoing timestamp right BEFORE sending the packet to avoid races that could result in negative latency. We aren't Stadia + + :param pks: SuperSocket instance to send/receive packets + :param pkt: Packet or iterable of packets to be sent. + :param timeout: How much time to wait after the last packet + has been sent. Defaults to None. + :param inter: Delay between two packets during sending. Defaults to 0. + :param verbose: Set verbosity level. Defaults to None. + :param chainCC: If True, KeyboardInterrupts will be forwarded. + Defaults to False. + :param retry: If positive, how many times to resend unanswered packets. + If negative, how many times to retry when no more packets + are answered. Defaults to 0. + :param multi: Whether to accept multiple answers for the same stimulus. + Defaults to False. + :param rcv_pks: If set, will be used instead of pks to receive packets. + Packets will still be sent through pks. + Defaults to None. + :param prebuild: Pre-build the packets before starting to send them. + Automatically enabled when a generator is passed as the + packet. Defaults to False. + :param _flood: _FloodGenerator object, internally used by `flood()` + methods. Defaults to None. + :param threaded: If True, packets will be sent in an individual thread. + Defaults to False. + :param session: A flow decoder used to handle the stream of packets. + Defaults to None. + :param chainEX: If True, exceptions during send will be forwarded. + Defaults to False. """ def __init__(self, pks, # type: SuperSocket @@ -692,7 +695,7 @@ def _interface_selection(iface, # type: Optional[_GlobInterfaceType] @conf.commands.register -def sr(x, # type: _PacketIterable +def sr(pkt, # type: _PacketIterable promisc=None, # type: Optional[bool] filter=None, # type: Optional[str] iface=None, # type: Optional[_GlobInterfaceType] @@ -703,13 +706,46 @@ def sr(x, # type: _PacketIterable # type: (...) -> Tuple[SndRcvList, PacketList] """ Send and receive packets at layer 3 + + :param pkt: Packet or iterable of packets to be sent. + :param promisc: Sets the socket in promisc mode, if True. + :param iface: Use a specific network interface, if provided. + :param filter: Filter string applied to the underlying socket. + :param nofilter: + :param timeout: How much time to wait after the last packet + has been sent. Defaults to None. + :param inter: Delay between two packets during sending. Defaults to 0. + :param verbose: Set verbosity level. Defaults to None. + :param chainCC: If True, KeyboardInterrupts will be forwarded. + Defaults to False. + :param retry: If positive, how many times to resend unanswered packets. + If negative, how many times to retry when no more packets + are answered. Defaults to 0. + :param multi: Whether to accept multiple answers for the same stimulus. + Defaults to False. + :param rcv_pks: If set, will be used instead of pks to receive packets. + Packets will still be sent through pks. + Defaults to None. + :param prebuild: Pre-build the packets before starting to send them. + Automatically enabled when a generator is passed as the + packet. Defaults to False. + :param _flood: _FloodGenerator object, internally used by `flood()` + methods. Defaults to None. + :param threaded: If True, packets will be sent in an individual thread. + Defaults to False. + :param session: A flow decoder used to handle the stream of packets. + Defaults to None. + :param chainEX: If True, exceptions during send will be forwarded. + Defaults to False. + :return: A tuple, consisting of two packet lists, one with + answered packets, the other with unanswered packets """ - iface, ipv6 = _interface_selection(iface, x) + iface, ipv6 = _interface_selection(iface, pkt) s = iface.l3socket(ipv6)( promisc=promisc, filter=filter, iface=iface, nofilter=nofilter, ) - result = sndrcv(s, x, *args, **kargs) + result = sndrcv(s, pkt, *args, **kargs) s.close() return result @@ -719,6 +755,38 @@ def sr1(*args, **kargs): # type: (*Any, **Any) -> Optional[Packet] """ Send packets at layer 3 and return only the first answer + + :param pkt: Packet or iterable of packets to be sent. + :param promisc: Sets the socket in promisc mode, if True. + :param iface: Use a specific network interface, if provided. + :param filter: Filter string applied to the underlying socket. + :param nofilter: + :param timeout: How much time to wait after the last packet + has been sent. Defaults to None. + :param inter: Delay between two packets during sending. Defaults to 0. + :param verbose: Set verbosity level. Defaults to None. + :param chainCC: If True, KeyboardInterrupts will be forwarded. + Defaults to False. + :param retry: If positive, how many times to resend unanswered packets. + If negative, how many times to retry when no more packets + are answered. Defaults to 0. + :param multi: Whether to accept multiple answers for the same stimulus. + Defaults to False. + :param rcv_pks: If set, will be used instead of pks to receive packets. + Packets will still be sent through pks. + Defaults to None. + :param prebuild: Pre-build the packets before starting to send them. + Automatically enabled when a generator is passed as the + packet. Defaults to False. + :param _flood: _FloodGenerator object, internally used by `flood()` + methods. Defaults to None. + :param threaded: If True, packets will be sent in an individual thread. + Defaults to False. + :param session: A flow decoder used to handle the stream of packets. + Defaults to None. + :param chainEX: If True, exceptions during send will be forwarded. + Defaults to False. + :return: A received Packet answering the sent packet, or None """ ans, _ = sr(*args, **kargs) if ans: @@ -727,7 +795,7 @@ def sr1(*args, **kargs): @conf.commands.register -def srp(x, # type: _PacketIterable +def srp(pkt, # type: _PacketIterable promisc=None, # type: Optional[bool] iface=None, # type: Optional[_GlobInterfaceType] iface_hint=None, # type: Optional[str] @@ -740,13 +808,50 @@ def srp(x, # type: _PacketIterable # type: (...) -> Tuple[SndRcvList, PacketList] """ Send and receive packets at layer 2 + + :param pkt: Packet or iterable of packets to be sent. + :param promisc: Sets the socket in promisc mode, if True. + :param iface: Use a specific network interface, if provided. + :param iface_hint: The interface used to connect to the host based on + the route information, if provided. + :param filter: Filter string applied to the underlying socket. + :param nofilter: + :param type: Type of the underlying socket + :param timeout: How much time to wait after the last packet + has been sent. Defaults to None. + :param inter: Delay between two packets during sending. Defaults to 0. + :param verbose: Set verbosity level. Defaults to None. + :param chainCC: If True, KeyboardInterrupts will be forwarded. + Defaults to False. + :param retry: If positive, how many times to resend unanswered packets. + If negative, how many times to retry when no more packets + are answered. Defaults to 0. + :param multi: Whether to accept multiple answers for the same stimulus. + Defaults to False. + :param rcv_pks: If set, will be used instead of pks to receive packets. + Packets will still be sent through pks. + Defaults to None. + :param prebuild: Pre-build the packets before starting to send them. + Automatically enabled when a generator is passed as the + packet. Defaults to False. + :param _flood: _FloodGenerator object, internally used by `flood()` + methods. Defaults to None. + :param threaded: If True, packets will be sent in an individual thread. + Defaults to False. + :param session: A flow decoder used to handle the stream of packets. + Defaults to None. + :param chainEX: If True, exceptions during send will be forwarded. + Defaults to False. + + :return: A tuple, consisting of two packet lists, one with + answered packets, the other with unanswered packets """ if iface is None and iface_hint is not None: iface = conf.route.route(iface_hint)[0] iface = resolve_iface(iface or conf.iface) s = iface.l2socket()(promisc=promisc, iface=iface, filter=filter, nofilter=nofilter, type=type) - result = sndrcv(s, x, *args, **kargs) + result = sndrcv(s, pkt, *args, **kargs) s.close() return result @@ -756,6 +861,41 @@ def srp1(*args, **kargs): # type: (*Any, **Any) -> Optional[Packet] """ Send and receive packets at layer 2 and return only the first answer + + :param pkt: Packet or iterable of packets to be sent. + :param promisc: Sets the socket in promisc mode, if True. + :param iface: Use a specific network interface, if provided. + :param iface_hint: The interface used to connect to the host based on + the route information, if provided. + :param filter: Filter string applied to the underlying socket. + :param nofilter: + :param type: Type of the underlying socket + :param timeout: How much time to wait after the last packet + has been sent. Defaults to None. + :param inter: Delay between two packets during sending. Defaults to 0. + :param verbose: Set verbosity level. Defaults to None. + :param chainCC: If True, KeyboardInterrupts will be forwarded. + Defaults to False. + :param retry: If positive, how many times to resend unanswered packets. + If negative, how many times to retry when no more packets + are answered. Defaults to 0. + :param multi: Whether to accept multiple answers for the same stimulus. + Defaults to False. + :param rcv_pks: If set, will be used instead of pks to receive packets. + Packets will still be sent through pks. + Defaults to None. + :param prebuild: Pre-build the packets before starting to send them. + Automatically enabled when a generator is passed as the + packet. Defaults to False. + :param _flood: _FloodGenerator object, internally used by `flood()` + methods. Defaults to None. + :param threaded: If True, packets will be sent in an individual thread. + Defaults to False. + :param session: A flow decoder used to handle the stream of packets. + Defaults to None. + :param chainEX: If True, exceptions during send will be forwarded. + Defaults to False. + :return: A received Packet answering the sent packet, or None """ ans, _ = srp(*args, **kargs) if len(ans) > 0: @@ -763,32 +903,8 @@ def srp1(*args, **kargs): return None -# Append doc -for sr_func in [srp, sr]: - if sr_func.__doc__ is not None: - sr_func.__doc__ += (_DOC_SNDRCV_PARAMS_HEAD + - _DOC_SNDRCV_PARAMS_BODY + - _DOC_SNDRCV_PARAMS_TAIL) - -for sr_func in [srp1, sr1]: - if sr_func.__doc__ is not None: - sr_func.__doc__ += (_DOC_SNDRCV_PARAMS_HEAD + - _DOC_SNDRCV_PARAMS_BODY + - _DOC_SNDRCV1_PARAMS_TAIL) - -# Append doc in SuperSocket -for sr_func in [SuperSocket.sr]: - if sr_func.__doc__ is not None: - sr_func.__doc__ += _DOC_SNDRCV_PARAMS_BODY + _DOC_SNDRCV_PARAMS_TAIL - -for sr_func in [SuperSocket.sr1]: - if sr_func.__doc__ is not None: - sr_func.__doc__ += _DOC_SNDRCV_PARAMS_BODY + _DOC_SNDRCV1_PARAMS_TAIL - - # SEND/RECV LOOP METHODS - def __sr_loop(srfunc, # type: Callable[..., Tuple[SndRcvList, PacketList]] pkts, # type: _PacketIterable prn=lambda x: x[1].summary(), # type: Optional[Callable[[QueryAnswer], Any]] # noqa: E501 From e8f3572ebe5940cfab556fd4e8089c92a008c2e2 Mon Sep 17 00:00:00 2001 From: Nils Weiss Date: Wed, 1 Nov 2023 15:51:51 +0100 Subject: [PATCH 8/9] revert changes --- scapy/contrib/automotive/uds.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/scapy/contrib/automotive/uds.py b/scapy/contrib/automotive/uds.py index c388e0f321b..bbe0316144c 100644 --- a/scapy/contrib/automotive/uds.py +++ b/scapy/contrib/automotive/uds.py @@ -1013,6 +1013,8 @@ class UDS_RDTCI(Packet): class DTC(Packet): name = 'Diagnostic Trouble Code' + dtc_descriptions = {} # Customize this dictionary for each individual ECU / OEM + fields_desc = [ BitEnumField("system", 0, 2, { 0: "Powertrain", @@ -1032,7 +1034,7 @@ def extract_padding(self, s): return '', s -class DTC_Status(Packet): +class DTCAndStatusRecord(Packet): name = 'DTC and status record' fields_desc = [ PacketField("dtc", None, pkt_cls=DTC), @@ -1043,6 +1045,26 @@ def extract_padding(self, s): return '', s +class DTCExtendedData(Packet): + name = 'Diagnostic Trouble Code Extended Data' + dataTypes = ObservableDict() + + fields_desc = [ + ByteEnumField("data_type", 0, dataTypes), + XByteField("record", 0) + ] + + def extract_padding(self, s): + return '', s + + +class DTCExtendedDataRecord(Packet): + fields_desc = [ + PacketField("dtcAndStatus", None, pkt_cls=DTCAndStatusRecord), + PacketListField("extendedData", None, pkt_cls=DTCExtendedData) + ] + + class UDS_RDTCIPR(Packet): name = 'ReadDTCInformationPositiveResponse' fields_desc = [ @@ -1063,7 +1085,7 @@ class UDS_RDTCIPR(Packet): lambda pkt: pkt.reportType in [0x01, 0x07, 0x11, 0x12]), ConditionalField(PacketListField('DTCAndStatusRecord', None, - pkt_cls=DTC_Status), + pkt_cls=DTCAndStatusRecord), lambda pkt: pkt.reportType in [0x02, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x13, 0x15]), From 1ee317caaee1f8df36c6854a77a3343c81851249 Mon Sep 17 00:00:00 2001 From: Nils Weiss Date: Thu, 2 Nov 2023 09:44:34 +0100 Subject: [PATCH 9/9] fix build --- scapy/sendrecv.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py index 2e8be40d281..ac2cb6765f9 100644 --- a/scapy/sendrecv.py +++ b/scapy/sendrecv.py @@ -114,8 +114,8 @@ class debug: # Append doc in SuperSocket -SuperSocket.sr.__doc__ += _DOC_SNDRCV_PARAMS_BODY + _DOC_SNDRCV_PARAMS_TAIL # type: ignore -SuperSocket.sr1.__doc__ += _DOC_SNDRCV_PARAMS_BODY + _DOC_SNDRCV1_PARAMS_TAIL # type: ignore +SuperSocket.sr.__doc__ += _DOC_SNDRCV_PARAMS_BODY + _DOC_SNDRCV_PARAMS_TAIL # type: ignore # noqa: E501 +SuperSocket.sr1.__doc__ += _DOC_SNDRCV_PARAMS_BODY + _DOC_SNDRCV1_PARAMS_TAIL # type: ignore # noqa: E501 _GlobSessionType = Union[Type[DefaultSession], DefaultSession] @@ -756,7 +756,7 @@ def sr1(*args, **kargs): """ Send packets at layer 3 and return only the first answer - :param pkt: Packet or iterable of packets to be sent. + :param pkt: Packet or iterable of packets to be sent. :param promisc: Sets the socket in promisc mode, if True. :param iface: Use a specific network interface, if provided. :param filter: Filter string applied to the underlying socket.