diff --git a/scapy/contrib/bgp.py b/scapy/contrib/bgp.py index a0a5fdadc55a7e1084625a2720eed8a5ae0b9853..87ea38f71e252d8da63cd914e92257cfe00ab293 100644 --- a/scapy/contrib/bgp.py +++ b/scapy/contrib/bgp.py @@ -607,28 +607,18 @@ class BGPCapability(six.with_metaclass(_BGPCapability_metaclass, Packet)): # Every BGP capability object inherits from BGPCapability. def haslayer(self, cls): - ret = 0 - if cls == BGPCapability: - # If cls is BGPCap (the parent class), check that the object is an - # instance of an existing BGP capability class. - for cap_class in _capabilities_registry: - if isinstance(self, _capabilities_registry[cap_class]): - ret = 1 - break - elif cls in _capabilities_registry and isinstance(self, cls): - ret = 1 - return ret - - def getlayer(self, cls, nb=1, _track=None): - layer = None - if cls == BGPCapability: - for cap_class in _capabilities_registry: - if isinstance(self, _capabilities_registry[cap_class]): - layer = self - break - else: - layer = Packet.getlayer(self, cls, nb, _track) - return layer + if cls == "BGPCapability": + if isinstance(self, BGPCapability): + return True + if issubclass(cls, BGPCapability): + if isinstance(self, cls): + return True + return super(BGPCapability, self).haslayer(cls) + + def getlayer(self, cls, nb=1, _track=None, _subclass=True, **flt): + return super(BGPCapability, self).getlayer( + cls, nb=nb, _track=_track, _subclass=True, **flt + ) def post_build(self, p, pay): length = 0 diff --git a/scapy/contrib/bgp.uts b/scapy/contrib/bgp.uts index 521abe07c42f71309d5fe2a4dcead1f7e3ddc905..55eb506ba5e442a44fc82032c2eb980a822f6c8e 100644 --- a/scapy/contrib/bgp.uts +++ b/scapy/contrib/bgp.uts @@ -119,10 +119,12 @@ except _BGPInvalidDataException: True = BGPCapability - Test haslayer() -BGPCapFourBytesASN().haslayer(BGPCapability) == True +assert BGPCapFourBytesASN().haslayer(BGPCapability) +assert BGPCapability in BGPCapFourBytesASN() = BGPCapability - Test getlayer() -isinstance(BGPCapFourBytesASN().getlayer(BGPCapability), BGPCapFourBytesASN) +assert isinstance(BGPCapFourBytesASN().getlayer(BGPCapability), BGPCapFourBytesASN) +assert isinstance(BGPCapFourBytesASN()[BGPCapability], BGPCapFourBytesASN) ############################ BGPCapMultiprotocol ############################## diff --git a/scapy/layers/eap.py b/scapy/layers/eap.py index b441ff446d83af59506e04710e10a0df29d76689..f5b49764910528915b4f77a381470f78d517b003 100644 --- a/scapy/layers/eap.py +++ b/scapy/layers/eap.py @@ -238,26 +238,17 @@ class EAP(Packet): return cls def haslayer(self, cls): - ret = 0 - if cls == EAP: - for eap_class in EAP.registered_methods.values(): - if isinstance(self, eap_class): - ret = 1 - break - elif cls in list(EAP.registered_methods.values()) and isinstance(self, cls): - ret = 1 - return ret - - def getlayer(self, cls, nb=1, _track=None): - layer = None - if cls == EAP: - for eap_class in EAP.registered_methods.values(): - if isinstance(self, eap_class): - layer = self - break - else: - layer = Packet.getlayer(self, cls, nb, _track) - return layer + if cls == "EAP": + if isinstance(self, EAP): + return True + elif issubclass(cls, EAP): + if isinstance(self, cls): + return True + return super(EAP, self).haslayer(cls) + + def getlayer(self, cls, nb=1, _track=None, _subclass=True, **flt): + return super(EAP, self).getlayer(cls, nb=nb, _track=_track, + _subclass=True, **flt) def answers(self, other): if isinstance(other, EAP): diff --git a/scapy/layers/ntp.py b/scapy/layers/ntp.py index 2e75067e27a55278e5c803d85e9a40a0fd6d6e96..f8dbe16b45c95e9f6fe0c42152bfc2b25489b95e 100644 --- a/scapy/layers/ntp.py +++ b/scapy/layers/ntp.py @@ -95,14 +95,6 @@ class TimeStampField(FixedPointField): return FixedPointField.i2m(self, pkt, val) -def get_cls(name, fallback_cls=conf.raw_layer): - """ - Returns class named "name" if it exists, fallback_cls otherwise. - """ - - return globals().get(name, fallback_cls) - - ############################################################################# ##### NTP ############################################################################# @@ -172,42 +164,22 @@ _kiss_codes = { # Used by _ntp_dispatcher to instantiate the appropriate class -_ntp_cls_by_mode = { - 0: "NTPHeader", - 1: "NTPHeader", - 2: "NTPHeader", - 3: "NTPHeader", - 4: "NTPHeader", - 5: "NTPHeader", - 6: "NTPControl", - 7: "NTPPrivate" -} - - def _ntp_dispatcher(payload): """ Returns the right class for a given NTP packet. """ - - cls = conf.raw_layer - # By default, calling NTP() will build a NTP packet as defined in RFC 5905 # (see the code of NTPHeader). Use NTPHeader for extension fields and MAC. if payload is None: - cls = get_cls("NTPHeader") - + return NTPHeader else: length = len(payload) if length >= _NTP_PACKET_MIN_SIZE: first_byte = orb(payload[0]) - # Extract NTP mode - mode_mask = 0x07 - mode = first_byte & mode_mask - - cls = get_cls(_ntp_cls_by_mode.get(mode)) - - return cls + mode = first_byte & 7 + return {6: NTPControl, 7: NTPPrivate}.get(mode, NTPHeader) + return conf.raw_layer class NTP(Packet): @@ -238,38 +210,18 @@ class NTP(Packet): # NTPHeader, NTPControl and NTPPrivate are NTP packets. # This might help, for example when reading a pcap file. def haslayer(self, cls): - ntp_classes = [ - get_cls("NTPHeader"), - get_cls("NTPControl"), - get_cls("NTPPrivate") - ] - ret = 0 - if cls == NTP: - # If cls is NTP (the parent class), check that the object is an - # instance of a NTP packet - for ntp_class in ntp_classes: - if isinstance(self, ntp_class): - ret = 1 - break - elif cls in ntp_classes and isinstance(self, cls): - ret = 1 - return ret - - def getlayer(self, cls, nb=1, _track=None): - ntp_classes = [ - get_cls("NTPHeader"), - get_cls("NTPControl"), - get_cls("NTPPrivate") - ] - layer = None - if cls == NTP: - for ntp_class in ntp_classes: - if isinstance(self, ntp_class): - layer = self - break - else: - layer = Packet.getlayer(self, cls, nb, _track) - return layer + """Specific: NTPHeader().haslayer(NTP) should return True.""" + if cls == "NTP": + if isinstance(self, NTP): + return True + elif issubclass(cls, NTP): + if isinstance(self, cls): + return True + return super(NTP, self).haslayer(cls) + + def getlayer(self, cls, nb=1, _track=None, _subclass=True, **flt): + return super(NTP, self).getlayer(cls, nb=nb, _track=_track, + _subclass=True, **flt) def mysummary(self): return self.sprintf("NTP v%ir,NTP.version%, %NTP.mode%") diff --git a/scapy/layers/radius.py b/scapy/layers/radius.py index 9b7507fbdbf60776032bcedf235a8de8babf926d..ada1cb4243857b5f0d5857e96ac2c3e134beac27 100644 --- a/scapy/layers/radius.py +++ b/scapy/layers/radius.py @@ -270,25 +270,17 @@ class RadiusAttribute(Packet): return cls def haslayer(self, cls): - if cls == RadiusAttribute: - for attr_class in RadiusAttribute.registered_attributes.values(): - if isinstance(self, attr_class): - return True - elif cls in RadiusAttribute.registered_attributes.values() and isinstance(self, cls): - return True - return False - - def getlayer(self, cls, nb=1, _track=None): - layer = None - if cls == RadiusAttribute: - for attr_class in RadiusAttribute.registered_attributes.values(): - if isinstance(self, attr_class): - layer = self - break - else: - layer = Packet.getlayer(self, cls, nb, _track) - return layer - + if cls == "RadiusAttribute": + if isinstance(self, RadiusAttribute): + return True + elif issubclass(cls, RadiusAttribute): + if isinstance(self, cls): + return True + return super(RadiusAttribute, self).haslayer(cls) + + def getlayer(self, cls, nb=1, _track=None, _subclass=True, **flt): + return super(RadiusAttribute, self).getlayer(cls, nb=nb, _track=_track, + _subclass=True, **flt) def post_build(self, p, pay): length = self.len diff --git a/scapy/packet.py b/scapy/packet.py index 326f15b1a538bd290fe23eac339f560f72853c32..89184d1724565d819f243f21a0b67b8a89e2f585 100644 --- a/scapy/packet.py +++ b/scapy/packet.py @@ -888,8 +888,16 @@ class Packet(six.with_metaclass(Packet_metaclass, BasePacket)): if ret: return ret return self.payload.haslayer(cls) - def getlayer(self, cls, nb=1, _track=None): - """Return the nb^th layer that is an instance of cls.""" + + def getlayer(self, cls, nb=1, _track=None, _subclass=False, **flt): + """Return the nb^th layer that is an instance of cls, matching flt +values. + + """ + if _subclass: + match = lambda cls1, cls2: issubclass(cls1, cls2) + else: + match = lambda cls1, cls2: cls1 == cls2 if isinstance(cls, int): nb = cls+1 cls = None @@ -897,14 +905,16 @@ class Packet(six.with_metaclass(Packet_metaclass, BasePacket)): ccls,fld = cls.split(".",1) else: ccls,fld = cls,None - if cls is None or self.__class__ == cls or self.__class__.__name__ == ccls: - if nb == 1: - if fld is None: - return self + if cls is None or match(self.__class__, cls) or self.__class__.__name__ == ccls: + if all(self.getfieldval(fldname) == fldvalue + for fldname, fldvalue in flt.iteritems()): + if nb == 1: + if fld is None: + return self + else: + return self.getfieldval(fld) else: - return self.getfieldval(fld) - else: - nb -=1 + nb -=1 for f in self.packetfields: fvalue_gen = self.getfieldval(f.name) if fvalue_gen is None: @@ -914,11 +924,13 @@ class Packet(six.with_metaclass(Packet_metaclass, BasePacket)): for fvalue in fvalue_gen: if isinstance(fvalue, Packet): track=[] - ret = fvalue.getlayer(cls, nb, _track=track) + ret = fvalue.getlayer(cls, nb=nb, _track=track, + _subclass=_subclass) if ret is not None: return ret nb = track[0] - return self.payload.getlayer(cls,nb,_track=_track) + return self.payload.getlayer(cls, nb=nb, _track=_track, + _subclass=_subclass, **flt) def firstlayer(self): q = self @@ -930,13 +942,11 @@ class Packet(six.with_metaclass(Packet_metaclass, BasePacket)): if isinstance(cls, slice): lname = cls.start if cls.stop: - ret = self.getlayer(cls.start, cls.stop) + ret = self.getlayer(cls.start, nb=cls.stop, **(cls.step or {})) else: - ret = self.getlayer(cls.start) - if ret is None and cls.step is not None: - ret = cls.step + ret = self.getlayer(cls.start, **(cls.step or {})) else: - lname=cls + lname = cls ret = self.getlayer(cls) if ret is None: if isinstance(lname, Packet_metaclass): @@ -1287,7 +1297,7 @@ class NoPayload(Packet): return isinstance(other, NoPayload) or isinstance(other, conf.padding_layer) def haslayer(self, cls): return 0 - def getlayer(self, cls, nb=1, _track=None): + def getlayer(self, cls, nb=1, _track=None, **flt): if _track is not None: _track.append(nb) return None diff --git a/test/regression.uts b/test/regression.uts index 43e5543a7b89f3448c4f76dd3131ee2a23a7a345..c706feed4a1583cd0282d953ddee208b6be731af 100644 --- a/test/regression.uts +++ b/test/regression.uts @@ -482,6 +482,37 @@ except IndexError: else: False += getlayer with a filter +~ getlayer IP +pkt = IP() / IP(ttl=3) / IP() +assert pkt[IP::{"ttl":3}].ttl == 3 +assert pkt.getlayer(IP, ttl=3).ttl == 3 + += specific haslayer and getlayer implementations for NTP +~ haslayer getlayer NTP +pkt = IP() / UDP() / NTPHeader() +assert NTP in pkt +assert pkt.haslayer(NTP) +assert isinstance(pkt[NTP], NTPHeader) +assert isinstance(pkt.getlayer(NTP), NTPHeader) + += specific haslayer and getlayer implementations for EAP +~ haslayer getlayer EAP +pkt = Ether() / EAPOL() / EAP_MD5() +assert EAP in pkt +assert pkt.haslayer(EAP) +assert isinstance(pkt[EAP], EAP_MD5) +assert isinstance(pkt.getlayer(EAP), EAP_MD5) + += specific haslayer and getlayer implementations for RadiusAttribute +~ haslayer getlayer RadiusAttribute +pkt = RadiusAttr_EAP_Message() +assert RadiusAttribute in pkt +assert pkt.haslayer(RadiusAttribute) +assert isinstance(pkt[RadiusAttribute], RadiusAttr_EAP_Message) +assert isinstance(pkt.getlayer(RadiusAttribute), RadiusAttr_EAP_Message) + + = equality ~ basic w=Ether()/IP()/UDP(dport=53) @@ -8645,6 +8676,11 @@ Dot11(type=0, subtype=1).answers(query) == True assert Dot11Elt(info="scapy").summary() == "SSID='scapy'" assert Dot11Elt(ID=1).mysummary() == "" += Multiple Dot11Elt layers +pkt = Dot11() / Dot11Beacon() / Dot11Elt(ID="Rates") / Dot11Elt(ID="SSID", info="Scapy") +assert pkt[Dot11Elt::{"ID": 0}].info == "Scapy" +assert pkt.getlayer(Dot11Elt, ID=0).info == "Scapy" + = Dot11WEP - build ~ crypto conf.wepkey = ""