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