diff --git a/scapy/utils.py b/scapy/utils.py index 0699c955e56b3b8a6dff99a3f36f609921b7c83e..0a50399974539647087ece98b95f111f8ade0c45 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -532,6 +532,17 @@ count: read only <count> packets""" with PcapReader(filename) as fdesc: return fdesc.read_all(count=count) +def rdpcapng(filename, count=-1): + """Read a pcap file and return a packet list +count: read only <count> packets""" + ## Does not work with Python <= 2.5. Use this implementation as + ## soon as we drop support for Python 2.5. + # with PcapNgReader(filename) as fdesc: + # return fdesc.read_all(count=count) + fdesc = PcapNgReader(filename) + result = fdesc.read_all(count=count) + fdesc.close() + return result class RawPcapReader: @@ -547,19 +558,20 @@ class RawPcapReader: magic = self.f.read(4) if magic == "\xa1\xb2\xc3\xd4": #big endian self.endian = ">" - elif magic == "\xd4\xc3\xb2\xa1": #little endian + elif magic == "\xd4\xc3\xb2\xa1": #little endian self.endian = "<" else: - raise Scapy_Exception("Not a pcap capture file (bad magic)") + raise Scapy_Exception( + "Not a pcap capture file (bad magic: %r)" % magic + ) hdr = self.f.read(20) if len(hdr)<20: raise Scapy_Exception("Invalid pcap file (too short)") - vermaj,vermin,tz,sig,snaplen,linktype = struct.unpack(self.endian+"HHIIII",hdr) - + vermaj, vermin, tz, sig, snaplen, linktype = struct.unpack( + self.endian + "HHIIII", hdr + ) self.linktype = linktype - - def __iter__(self): return self @@ -654,7 +666,104 @@ class PcapReader(RawPcapReader): return plist.PacketList(res,name = os.path.basename(self.filename)) def recv(self, size=MTU): return self.read_packet(size=size) - + + +class RawPcapNgReader(RawPcapReader): + """A stateful pcapng reader. Each packet is returned as a + string. + + """ + + def __init__(self, filename): + self.filename = filename + # A list of (linktype, snaplen); will be populated by IDBs. + self.interfaces = [] + self.blocktypes = { + 1: self.read_block_idb, + 6: self.read_block_epb, + } + try: + self.f = gzip.open(filename,"rb") + magic = self.f.read(4) + except IOError: + self.f = open(filename,"rb") + magic = self.f.read(4) + if magic != "\n\r\r\n": # PcapNg: + raise Scapy_Exception( + "Not a pcapng capture file (bad magic: %r)" % magic + ) + # see https://github.com/pcapng/pcapng + blocklen, magic = self.f.read(4), self.f.read(4) + if magic == "\x1a\x2b\x3c\x4d": + self.endian = ">" + elif magic == "\x4d\x3c\x2b\x1a": + self.endian = "<" + else: + raise Scapy_Exception("Not a pcapng capture file (bad magic)") + self.f.seek(0) + + def read_packet(self, size=MTU): + """Read blocks until it reaches either EOF or a packet, and + returns None or (packet, (linktype, sec, usec, wirelen)), + where packet is a string. + + """ + while True: + try: + blocktype, blocklen = struct.unpack(self.endian + "2I", + self.f.read(8)) + except struct.error: + return None + block = self.f.read(blocklen - 12) + try: + if (blocklen,) != struct.unpack(self.endian + 'I', + self.f.read(4)): + raise Scapy_Exception( + "Invalid pcapng block (bad blocklen)" + ) + except struct.error: + return None + res = self.blocktypes.get(blocktype, + lambda block, size: None)(block, size) + if res is not None: + return res + + def read_block_idb(self, block, _): + """Interface Description Block""" + self.interfaces.append(struct.unpack(self.endian + "HxxI", block[:8])) + + def read_block_epb(self, block, size): + """Enhanced Packet Block""" + intid, sec, usec, caplen, wirelen = struct.unpack(self.endian + "5I", + block[:20]) + return (block[20:20 + caplen][:size], + (self.interfaces[intid][0], sec, usec, wirelen)) + + +class PcapNgReader(RawPcapNgReader): + def __init__(self, filename): + RawPcapNgReader.__init__(self, filename) + def read_packet(self, size=MTU): + rp = RawPcapNgReader.read_packet(self, size=size) + if rp is None: + return None + s, (linktype, sec, usec, wirelen) = rp + try: + p = conf.l2types[linktype](s) + except KeyboardInterrupt: + raise + except: + if conf.debug_dissector: + raise + p = conf.raw_layer(s) + p.time = sec+0.000001*usec + return p + def read_all(self,count=-1): + res = RawPcapNgReader.read_all(self, count) + import plist + return plist.PacketList(res, name=os.path.basename(self.filename)) + def recv(self, size=MTU): + return self.read_packet() class RawPcapWriter: