Source code: com/voytechs/jnetstream/io/PcapInputStream.java
1 /*
2 * File: PcapInputStream.java
3 * Auth: Mark Bednarczyk
4 * Date: 2003-06-30
5 * Id: $Id: PcapInputStream.java,v 1.1.1.1 2003/09/22 16:32:08 voytechs Exp $
6 ********************************************
7 Copyright (C) 2003 Mark Bednarczyk
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 ********************************************
23 * $Log: PcapInputStream.java,v $
24 * Revision 1.1.1.1 2003/09/22 16:32:08 voytechs
25 * Initial import.
26 *
27 */
28 package com.voytechs.jnetstream.io;
29
30 import com.voytechs.jnetstream.codec.*;
31
32 import com.voytechs.jnetstream.protocol.layer2.*;
33 import com.voytechs.jnetstream.protocol.layer3.*;
34 import com.voytechs.jnetstream.protocol.layer4.*;
35
36 import com.voytechs.jnetanalyzer.tcp.*;
37
38 import java.lang.*;
39 import java.util.*;
40 import java.io.*;
41 import java.io.FileNotFoundException;
42 import java.sql.Timestamp;
43
44 /**
45 *
46 * Structure of TCPDump file.<BR>
47 <CODE>struct pcap_file_header {<BR>
48 bpf_u_int32 magic;<BR>
49 u_short version_major;<BR>
50 u_short version_minor;<BR>
51 bpf_int32 thiszone; // gmt to local correction //<BR>
52 bpf_u_int32 sigfigs; // accuracy of timestamps //<BR>
53 bpf_u_int32 snaplen; // max length saved portion of each pkt //<BR>
54 bpf_u_int32 linktype; // data link type (LINKTYPE_*) //<BR>
55 };</CODE><BR><BR>
56
57 Now per each packet:<BR>
58 <CODE>
59 struct pcap_pkthdr {<BR>
60 struct timeval ts; // time stamp //<BR>
61 bpf_u_int32 caplen; // length of portion present //<BR>
62 bpf_u_int32 len; // length this packet (off wire) //<BR>
63 };<BR>
64 </CODE>
65 */
66 public class PcapInputStream
67 extends PacketInputStream {
68
69 /* Internal attributes */
70 private static final boolean debug = false;
71
72 private static final int PCAP_MAGIC_NUMBER1 = 0xa1b2c3d4;
73 private static final int PCAP_MAGIC_NUMBER2 = 0xa1b2cd34;
74
75 private int pcapMagicNumber;
76 private int pcapMajorVer;
77 private int pcapMinorVer;
78 private int pcapTimezone;
79 private int pcapTimestampAccuracy;
80 private long pcapPacketLength;
81 private long pcapSnaplen;
82 private int pcapLinktype;
83
84 private long pcapPacketCaptureSecs;
85 private int pcapPacketCaptureNanos;
86
87
88 /**
89 *
90 * @param
91 * @exception
92 */
93 public PcapInputStream(InputStream pcapFileFormatInputStream)
94 throws
95 IOException,
96 EOPacketStream,
97 StreamFormatException {
98
99 super(new BitStackInputStream(pcapFileFormatInputStream));
100 initPacketStream();
101
102 super.captureDeviceFilename = "stream: pcapFileFormat";
103 }
104
105 /**
106 * Opens up the given file and starts processing the data.
107 * @param filename Filename of the file in PCAP fileformat to open.
108 * @exception
109 */
110 public PcapInputStream(String filename)
111 throws
112 FileNotFoundException,
113 IOException,
114 EOPacketStream,
115 StreamFormatException {
116
117 /**
118 * Open filestream for reading and pass it on.
119 */
120 super(new BitStackInputStream(new FileInputStream(filename)));
121 initPacketStream();
122
123 super.captureDeviceFilename = "file: " + filename;
124 }
125
126 /**
127 * Since we know we are dealing with a Pcap-file formatted stream
128 * (i.e. FileInputStream("pcap-capturefile.pcap")) we initialize
129 * the ProtocolInputStream using values extracted from the pcap stream.
130 *
131 * First we extract the "pcap_file_heaer" as specified above.
132 */
133 protected void initPacketStream()
134 throws
135 IOException,
136 EOPacketStream,
137 StreamFormatException {
138
139 /**
140 * Needed to temporarily allow reading of data from the stream so
141 * we can read in the headers.
142 */
143 setForceRead(true);
144
145 try {
146 pcapMagicNumber = readIntLittleEndian();
147
148 if(pcapMagicNumber != PCAP_MAGIC_NUMBER1 &&
149 pcapMagicNumber != PCAP_MAGIC_NUMBER2) {
150
151 throw new StreamFormatException(
152 "Stream in non-PCAP format. Invalid magic number at the begining" +
153 " of the file." +
154 "(expected=" + Integer.toHexString(PCAP_MAGIC_NUMBER1) +
155 " or expected=" + Integer.toHexString(PCAP_MAGIC_NUMBER2) +
156 " got=" + Integer.toHexString(pcapMagicNumber) +
157 ")"
158 );
159 }
160
161 pcapMajorVer = readUnsignedShortLittleEndian();
162 pcapMinorVer = readUnsignedShortLittleEndian();
163 pcapTimezone = readIntLittleEndian();
164 pcapTimestampAccuracy = readIntLittleEndian();
165 pcapSnaplen = readIntLittleEndian();
166 pcapLinktype = readIntLittleEndian();
167 }
168 catch(EOPacket eop) {
169 // Should never happen. We are not in the packet data stream at this
170 // point.
171 throw new IOException("Packet data protection exception ocurred in initPacketStream() method. Very unexpected.");
172 }
173
174 setForceRead(false);
175
176 /**
177 * Lastly initialize the system specific things we know.
178 */
179 captureDeviceOS = System.getProperty("os.name");
180 captureDeviceOS += " " + System.getProperty("os.version");
181 captureDeviceArch += " " + System.getProperty("os.arch");
182 }
183
184 /**
185 * Read pre-packet header from stream. With basic info about the next
186 * packet to follow.
187 * this is called for every packet in the stream. Main purpose is to
188 * get packet-data length (or length of captured packet) and the
189 * capture time of the packet.
190 */
191 protected void readPacketPreHeader()
192 throws
193 IOException,
194 EOPacketStream,
195 StreamFormatException{
196
197 /**
198 * Needed to temporarily allow reading of data from the stream so
199 * we can read in the headers.
200 */
201 setForceRead(true);
202
203 try {
204
205 if(debug)
206 System.out.println("readPacketPreHeader(): "
207 + StreamUtil.toString(position()));
208
209 // secs.nanos (this is time)
210 pcapPacketCaptureSecs = (long)readIntLittleEndian() * 1000;
211 super.packetCaptureTimestamp = new Timestamp(pcapPacketCaptureSecs);
212 pcapPacketCaptureNanos = readIntLittleEndian() * 1000;
213 super.packetCaptureTimestamp.setNanos(pcapPacketCaptureNanos);
214
215
216 // packet length in the stream.
217 super.packetSnaplen = (long)readIntLittleEndian();
218 pcapPacketLength = super.packetSnaplen;
219 super.recordLength = super.packetSnaplen;
220
221 // Packet length on the wire. Doesn't account for snaplen
222 super.packetLength = readIntLittleEndian();
223
224
225 readIntLittleEndian(); // some unknown data
226 readIntLittleEndian(); // some unknown data
227 }
228 catch(IllegalArgumentException iae) {
229 /*
230 * Normally caused by Timestamp.setNanos when stream does not contain
231 * right data and we use an invalid nanos value. This exception will
232 * be caused by invalid stream format. Could be corrupt stream or
233 * incomplete file.
234 */
235 throw new StreamFormatException(
236 "Invalid data in the stream. Value is outside the valid range of " +
237 "standard primitive type timestamp.nanos. This can only be caused " +
238 " by corrupt or invalid data stream."
239 );
240 }
241 catch(EOPacket eop) {
242 /*
243 * Should never happen since we are in-between packet data and
244 * are reading packet PCAP pre-header.
245 */
246 throw new IOException("Packet data protection exception ocurred in readPacketPreHeader() method. Very unexpected.");
247 }
248
249 setForceRead(false);
250 }
251
252 public String toString() {
253 String s = "";
254
255 if(debug) {
256 s += "pcapMagicNumber=" + Integer.toHexString(pcapMagicNumber);
257 s += ", pcapMajorVer=" + pcapMajorVer;
258 s += ", pcapMinorVer=" + pcapMinorVer;
259 s += ", pcapTimezone=" + pcapTimezone;
260 s += ", pcapTimestampAccuracy=" + pcapTimestampAccuracy;
261 s += ", pcapSnaplen=" + pcapSnaplen;
262 s += ", pcapLinktype=" + pcapLinktype;
263 s += ", timestamp=" + Long.toHexString(pcapPacketCaptureSecs);
264 s += "." + Long.toHexString(pcapPacketCaptureNanos);
265 }
266
267 return(s);
268 }
269
270 /**
271 * Returns the magic number found at the beginning of the PCAP file.
272 */
273 public int getPcapMagicNumber() { return(pcapMagicNumber); }
274
275 /**
276 * Returns the major version number of the PCAP file.
277 */
278 public int getPcapMajorVer() { return(pcapMajorVer); }
279
280 /**
281 * Returns the minor version number of the PCAP file.
282 */
283 public int getPcapMinorVer() { return(pcapMinorVer); }
284
285 /**
286 * Returns the timezone found in the PCAP file. The timezone is used
287 * for proper timestamp calculations of the captured packet.
288 */
289 public int pcapTimezone() { return(pcapTimezone); }
290
291 /**
292 * Returns the timestamp accuracy found in the PCAP file.
293 */
294 public int pcapTimestampAccuracy() { return(pcapTimestampAccuracy); }
295
296 /**
297 * Retuns the SNAP len of the capture file. This is the number of bytes
298 * that we saved, not captured. i.e. first 128 bytes.
299 */
300 public long pcapSnaplen() { return(pcapSnaplen); }
301
302 /**
303 * The link type of the first header in the packet. Normally this is type 2 (ethernet)
304 * but any of the others are possible as well.
305 */
306 public int pcapLinktype() { return(pcapLinktype); }
307
308 /**
309 * Number of raw number of the capture file. JNetStream library multiplies this number
310 * by factor of 1000 before returning it.
311 * <BR><BR>
312 * This value changes from packet to packet.
313 */
314 public long pcapPacketCaptureSecs() { return(pcapPacketCaptureSecs); }
315
316 /**
317 * Number of naono seconds found in the raw data file. This number is also
318 * multiplied by 1000.
319 * <BR><BR>
320 * This value changes from packet to packet.
321 */
322 public int pcapPacketCaptureNanos() { return(pcapPacketCaptureNanos); }
323
324
325
326 /**
327 * Test function for PcapInputStream
328 * @param args command line arguments
329 */
330 public static void main(String [] args) {
331
332 String file = "abc";
333
334 if(args.length > 0)
335 file = args[0];
336
337 Hashtable perm = new Hashtable();
338 TCPAnalyzer analyzer = new TCPAnalyzer();
339
340 try {
341 PcapInputStream pcap;
342
343 if(file.equals("-"))
344 pcap = new PcapInputStream(System.in);
345 else
346 pcap = new PcapInputStream(file);
347
348 System.out.println("Capture Device Address: " +
349 pcap.getCaptureDeviceAddress());
350 System.out.println("Capture Device OS: " + pcap.getCaptureDeviceOS());
351 System.out.println("Capture Device Arch: " + pcap.getCaptureDeviceArch());
352 System.out.println("Capture Device Filename: " + pcap.getCaptureDeviceFilename());
353 System.out.println("Is this capture live? " + pcap.isCaptureLive());
354 System.out.println("-------------");
355
356
357 for(int i = 0; ; i ++) {
358 try {
359
360 /*
361 * The way the example is setup there are 2 ways of running this loop.
362 *
363 * 1) is to call nextPacket() on every turn at the top. This will effectively
364 * skip any unread bytes until the end of the current packet (not the stream).
365 * After all of the data has been skipped, the PacketInputStream will read any
366 * packet data description headers expected in the stream, ie. what the length
367 * of the packet will be in the stream and the capture time of the packet.
368 *
369 * 2) Second method is to not do a nextPacket() call until the first read from
370 * the packet stream will generate a EOPacket exception. The exception is caught is
371 * the catch() statement below but still within the loop and there nextPacket() call
372 * executed and the loop starts over, but on the second time around the stream
373 * is ready for reading packet data.
374 */
375 // pcap.isReady();
376 pcap.nextPacket();
377
378
379
380 System.out.println("------------- " + (i+1) + " --------------");
381
382
383 MutablePacket pkt = new PacketImpl(perm, pcap);
384 Ethernet2 ethernet = new Ethernet2(pcap);
385 pkt.addHeader(ethernet);
386 // System.out.println(ethernet.getShortName() + ".proto=" + Integer.toHexString(ethernet.proto));
387
388
389 if(ethernet.proto == 0x800) {
390 IPv4 ip = new IPv4(pcap);
391 pkt.addHeader(ip);
392 System.out.print("[" + ip.src + "," + ip.dst + "]");
393 System.out.print("=" + ip.len);
394
395
396 pcap.push();
397
398 if(ip.proto == 6) {
399 TCP tcp = new TCP(pcap);
400 pkt.addHeader(tcp);
401 System.out.print(" " + tcp.src);
402 System.out.print("->" + tcp.dst);
403 System.out.print(" S=" + tcp.seq);
404 System.out.print(" A=" + tcp.ack);
405
406 System.out.println();
407
408 analyzer.processPacket((Packet)pkt);
409 }
410 else {
411 System.out.println("Not TCP protocol");
412 }
413
414 pcap.pop();
415 System.out.println(TCP.toString(pcap));
416 }
417
418 /*
419 * Push this position within the stream onto a stack of positions.
420 * The BitStackInputStream starts buffering from the first push.
421 * (Trust me the BitStackInputStream object is in the chain of all of
422 * InputStreams. Its internally instantiated by the PacketInputStream
423 * if it was not passed in directly.)
424 */
425 // pcap.push();
426
427 /*
428 * For kicks printout a few headers from the stream.
429 */
430 // System.out.println(Ethernet2.toString(pcap));
431 // System.out.println(IPv4.toString(pcap));
432 // System.out.println(TCP.toString(pcap));
433
434 /*
435 * Now pop the previous pushed position off of the stack in
436 * BitStackInputStream module. The stream will be rewounded back to
437 * that position and any subsequent read() calls will read from the
438 * buffer. When all of the data is read from the buffer, addtional reads
439 * will result in data from the live stream.
440 */
441 // pcap.pop();
442
443 /*
444 * Use this utility static method to print the contents of the
445 * entire packet, the same one that just printed out the headers above,
446 * but this time printout raw values in HEX. Since the data is rewound
447 * the values used by this print routine are from the stream. Because
448 * of the pop() call above, you can assume that this data is comming from
449 * a buffer, but utilizing the true stream interface.
450 */
451 // BitDataInputStream.printStream(pcap, pcap.getPacketLength());
452
453
454 /*
455 * Skip all the remaining bytes in the packet.
456 * Ofcournse pcap.skip(nBytes) is much more efficient.
457 */
458 // while(pcap.readFromPacket() != -1);
459 }
460 catch(EOPacket eop) {
461 System.out.println("Unexpected EOF Packet");
462 pcap.nextPacket();
463
464 System.out.println();
465 System.out.flush();
466 }
467 }
468 }
469 catch(StreamFormatException sfe) {
470 System.out.println(sfe);
471 System.exit(1);
472 }
473 catch(EOPacketStream eos) {
474 System.out.println();
475 System.out.println("DONE!");
476 }
477 catch(FileNotFoundException e) {
478 System.out.println(e);
479 System.exit(1);
480 }
481 catch(IOException ioe) {
482 System.out.println(ioe);
483 ioe.printStackTrace();
484 System.exit(1);
485 }
486
487 System.out.println(analyzer.toString());
488 }
489
490 } /* END OF: PcapInputStream */