Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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 */