xref: /libbtbb/python/pcaptools/pcapdump/pcapdump.py (revision 209acb71879f8217753a8fe9c1dea1aa65f3d819)
1# Copyright 2009 Joshua Wright
2#
3# This file is part of gr-bluetooth
4#
5# gr-bluetooth is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2, or (at your option)
8# any later version.
9#
10# gr-bluetooth is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with gr-bluetooth; see the file COPYING.  If not, write to
17# the Free Software Foundation, Inc., 51 Franklin Street,
18# Boston, MA 02110-1301, USA.
19
20import struct
21import time
22
23PCAPH_MAGIC_NUM = 0xa1b2c3d4
24PCAPH_VER_MAJOR = 2
25PCAPH_VER_MINOR = 4
26PCAPH_THISZONE = 0
27PCAPH_SIGFIGS = 0
28PCAPH_SNAPLEN = 65535
29
30class PcapReader:
31
32    def __init__(self, savefile):
33        '''
34        Opens the specified file, validates a libpcap header is present.
35        @type savefile: String
36        @param savefile: Input libpcap filename to open
37        @rtype: None
38        '''
39        PCAPH_LEN = 24
40        self.__fh = open(savefile, mode='rb')
41        self._pcaphsnaplen = 0
42        header = self.__fh.read(PCAPH_LEN)
43
44        # Read the first 4 bytes for the magic number, determine endianness
45        magicnum = struct.unpack("I", header[0:4])[0]
46        if magicnum != 0xd4c3b2a1:
47            # Little endian
48            self.__endflag = "<"
49        elif magicnum == 0xa1b2c3d4:
50            # Big endign
51            self.__endflag = ">"
52        else:
53            raise Exception('Specified file is not a libpcap capture')
54
55        pcaph = struct.unpack("%sIHHIIII"%self.__endflag, header)
56        if pcaph[1] != PCAPH_VER_MAJOR and pcaph[2] != PCAPH_VER_MINOR \
57                and pcaph[3] != PCAPH_THISZONE and pcaph[4] != PCAPH_SIGFIGS \
58                and pcaph[5] != PCAPH_SNAPLEN:
59            raise Exception('Unsupported pcap header format or version')
60
61        self._pcaphsnaplen = pcaph[5]
62        self._datalink = pcaph[6]
63
64    def datalink(self):
65        return self._datalink
66
67    def close(self):
68        '''
69        Closes the output packet capture; wrapper for pcap_close().
70        @rtype: None
71        '''
72        self.pcap_close()
73
74    def pcap_close(self):
75        '''
76        Closes the output packet capture.
77        @rtype: None
78        '''
79        self.__fh.close()
80
81    def pnext(self):
82        '''
83        Wrapper for pcap_next to mimic method for Daintree SNA
84        '''
85        return self.pcap_next()
86
87    def pcap_next(self):
88        '''
89        Retrieves the next packet from the capture file.  Returns a list of
90        [Hdr, packet] where Hdr is a list of [timestamp, snaplen, plen] and
91        packet is a string of the payload content.  Returns None at the end
92        of the packet capture.
93        @rtype: List
94        '''
95        # Read the next header block
96        PCAPH_RECLEN = 16
97        rechdrdata = self.__fh.read(PCAPH_RECLEN)
98
99        try:
100            rechdrtmp = struct.unpack("%sIIII"%self.__endflag, rechdrdata)
101        except struct.error:
102            return [None,None]
103
104        rechdr = [
105                float("%s.%s"%(rechdrtmp[0],rechdrtmp[1])),
106                rechdrtmp[2],
107                rechdrtmp[3]
108                ]
109        if rechdr[1] > rechdr[2] or rechdr[1] > self._pcaphsnaplen or rechdr[2] > self._pcaphsnaplen:
110            raise Exception('Corrupted or invalid libpcap record header (included length exceeds actual length)')
111
112        # Read the included packet length
113        frame = self.__fh.read(rechdr[1])
114        return [rechdr, frame]
115
116
117class PcapDumper:
118
119    def __init__(self, datalink, savefile):
120        '''
121        Creates a libpcap file using the specified datalink type.
122        @type datalink: Integer
123        @param datalink: Datalink type, one of DLT_* defined in pcap-bpf.h
124        @type savefile: String
125        @param savefile: Output libpcap filename to open
126        @rtype: None
127        '''
128        self.__fh = open(savefile, mode='wb')
129        self.__fh.write(''.join([
130            struct.pack("I", PCAPH_MAGIC_NUM),
131            struct.pack("H", PCAPH_VER_MAJOR),
132            struct.pack("H", PCAPH_VER_MINOR),
133            struct.pack("I", PCAPH_THISZONE),
134            struct.pack("I", PCAPH_SIGFIGS),
135            struct.pack("I", PCAPH_SNAPLEN),
136            struct.pack("I", datalink)
137            ]))
138
139    def pcap_dump(self, packet, ts_sec=None, ts_usec=None, orig_len=None):
140        '''
141        Appends a new packet to the libpcap file.  Optionally specify ts_sec
142        and tv_usec for timestamp information, otherwise the current time is
143        used.  Specify orig_len if your snaplen is smaller than the entire
144        packet contents.
145        @type ts_sec: Integer
146        @param ts_sec: Timestamp, number of seconds since Unix epoch.  Default
147        is the current timestamp.
148        @type ts_usec: Integer
149        @param ts_usec: Timestamp microseconds.  Defaults to current timestamp.
150        @type orig_len: Integer
151        @param orig_len: Length of the original packet, used if the packet you
152        are writing is smaller than the original packet.  Defaults to the
153        specified packet's length.
154        @type packet: String
155        @param packet: Packet contents
156        @rtype: None
157        '''
158
159        if ts_sec == None or ts_usec == None:
160            # There must be a better way here that I don't know -JW
161            s_sec, s_usec = str(time.time()).split(".")
162            ts_sec = int(s_sec)
163            ts_usec = int(s_usec)
164
165        if orig_len == None:
166            orig_len = len(packet)
167
168        plen = len(packet)
169
170        self.__fh.write(''.join([
171            struct.pack("I", ts_sec),
172            struct.pack("I", ts_usec),
173            struct.pack("I", orig_len),
174            struct.pack("I", plen),
175            packet
176            ]))
177
178        return
179
180
181    def close(self):
182        '''
183        Closes the output packet capture; wrapper for pcap_close().
184        @rtype: None
185        '''
186        self.pcap_close()
187
188    def pcap_close(self):
189        '''
190        Closed the output packet capture.
191        @rtype: None
192        '''
193        self.__fh.close()
194