diff --git a/scapy/utils.py b/scapy/utils.py index adaf63fe11ae2e70f26aceb8148966121c055a96..db2e4dab9db5f7b906f6fca8fe3b57d065d14be7 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -10,6 +10,7 @@ import gzip,zlib,cPickle import re,struct,array from arch import * from error import log_runtime +from base_classes import BasePacketList ########### @@ -453,13 +454,10 @@ def rdpcap(filename, count=-1): count: read only <count> packets""" return PcapReader(filename).read_all(count=count) -class PcapReader: - """A stateful pcap reader - - Based entirely on scapy.rdpcap(), this class allows for packets - to be dispatched without having to be loaded into memory all at - once - """ + + +class RawPcapReader: + """A stateful pcap reader. Each packet is returned as a string""" def __init__(self, filename): self.filename = filename @@ -479,17 +477,16 @@ class PcapReader: if len(hdr)<20: raise RuntimeWarning, "Invalid pcap file (too short)" vermaj,vermin,tz,sig,snaplen,linktype = struct.unpack(self.endian+"HHIIII",hdr) - self.LLcls = conf.l2types.get(linktype, Raw) - if self.LLcls == Raw: - warning("PcapReader: unkonwon LL type [%i]/[%#x]. Using Raw packets" % (linktype,linktype)) + + self.linktype = linktype + + def __iter__(self): return self def next(self): - """impliment the iterator protocol on a set of packets in a - pcap file - """ + """impliment the iterator protocol on a set of packets in a pcap file""" pkt = self.read_packet() if pkt == None: raise StopIteration @@ -504,18 +501,10 @@ class PcapReader: hdr = self.f.read(16) if len(hdr) < 16: return None - sec,usec,caplen,olen = struct.unpack(self.endian+"IIII", hdr) + sec,usec,caplen,wirelen = struct.unpack(self.endian+"IIII", hdr) s = self.f.read(caplen) - try: - p = self.LLcls(s) - except KeyboardInterrupt: - raise - except: - if conf.debug_dissector: - raise - p = Raw(s) - p.time = sec+0.000001*usec - return p + return s,(sec,usec,wirelen) # caplen = len(s) + def dispatch(self, callback): """call the specified callback routine for each packet read @@ -524,10 +513,8 @@ class PcapReader: that allows for easy launching of packet processing in a thread. """ - p = self.read_packet() - while p != None: + for p in self: callback(p) - p = self.read_packet() def read_all(self,count=-1): """return a list of all packets in the pcap file @@ -539,22 +526,54 @@ class PcapReader: if p is None: break res.append(p) - return PacketList(res,name = os.path.basename(self.filename)) + return res def recv(self, size): """ Emulate a socket """ - return self.read_packet() + return self.read_packet()[0] def fileno(self): return self.f.fileno() def close(self): return self.f.close() + + + +class PcapReader(RawPcapReader): + def __init__(self, filename): + RawPcapReader.__init__(self, filename) + try: + self.LLcls = conf.l2types[self.linktype] + except KeyError: + warning("PcapReader: unknown LL type [%i]/[%#x]. Using Raw packets" % (self.linktype,self.linktype)) + self.LLcls = Raw + def read_packet(self): + rp = RawPcapReader.read_packet(self) + if rp is None: + return None + s,(sec,usec,wirelen) = rp + + try: + p = self.LLcls(s) + except KeyboardInterrupt: + raise + except: + if conf.debug_dissector: + raise + p = Raw(s) + p.time = sec+0.000001*usec + return p + def read_all(self,count=-1): + res = RawPcapReader.read_all(self, count) + return PacketList(res,name = os.path.basename(self.filename)) + def recv(self, size): + return self.read_packet() -class PcapWriter: +class RawPcapWriter: """A stream PCAP writer with more control than wrpcap()""" def __init__(self, filename, linktype=None, gz=False, endianness="", append=False, sync=False): """ @@ -579,19 +598,12 @@ class PcapWriter: self.f = [open,gzip.open][gz](filename,append and "ab" or "wb", gz and 9 or bufsz) - - def fileno(self): return self.f.fileno() def _write_header(self, pkt): self.header_present=1 - if self.linktype == None: - if type(pkt) is list or type(pkt) is tuple: - pkt = pkt[0] - self.linktype = conf.l2types.get(pkt.__class__,1) - if self.append: # Even if prone to race conditions, this seems to be # safest way to tell whether the header is already present @@ -612,18 +624,28 @@ class PcapWriter: """ if not self.header_present: self._write_header(pkt) - for p in pkt: - self._write_packet(p) + if type(pkt) is str: + self._write_packet(pkt) + else: + for p in pkt: + self._write_packet(p) - def _write_packet(self, packet): + def _write_packet(self, packet, sec=None, usec=None, caplen=None, wirelen=None): """writes a single packet to the pcap file """ - s = str(packet) - l = len(s) - sec = int(packet.time) - usec = int((packet.time-sec)*1000000) - self.f.write(struct.pack(self.endian+"IIII", sec, usec, l, l)) - self.f.write(s) + if caplen is None: + caplen = len(packet) + if wirelen is None: + wirelen = caplen + if sec is None or usec is None: + t=time.time() + it = int(t) + if sec is None: + sec = it + if usec is None: + usec = int((t-it)*1000000) + self.f.write(struct.pack(self.endian+"IIII", sec, usec, caplen, wirelen)) + self.f.write(packet) if self.gz and self.sync: self.f.flush() @@ -632,6 +654,25 @@ class PcapWriter: def close(self): return self.f.close() +class PcapWriter(RawPcapWriter): + def _write_header(self, pkt): + if self.linktype == None: + if type(pkt) is list or type(pkt) is tuple or isinstance(pkt,BasePacketList): + pkt = pkt[0] + try: + self.linktype = conf.l2types[pkt.__class__] + except KeyError: + warning("PcapWriter: unknown LL type for %s. Using type 1 (Ethernet)" % pkt.__class__.__name__) + self.linktype = 1 + RawPcapWriter._write_header(self, pkt) + + def _write_packet(self, packet): + sec = int(packet.time) + usec = int((packet.time-sec)*1000000) + s = str(packet) + caplen = len(s) + RawPcapWriter._write_packet(self, s, sec, usec, caplen, caplen) + re_extract_hexcap = re.compile("^(0x[0-9a-fA-F]{2,}[ :\t]|(0x)?[0-9a-fA-F]{2,}:|(0x)?[0-9a-fA-F]{3,}[: \t]|) *(([0-9a-fA-F]{2} {,2}){,16})")