Source code: com/voytechs/jnetstream/io/SnoopInputStream.java
1 /*
2 * File: SnoopInputStream.java
3 * Auth: Mark Bednarczyk
4 * Date: 2003-06-30
5 * Id: $Id: SnoopInputStream.java,v 1.1.1.1 2003/09/22 16:32:09 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: SnoopInputStream.java,v $
24 * Revision 1.1.1.1 2003/09/22 16:32:09 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 * For structure of the Snoop file see RFC1761
47 * http://www.faqs.org/rfcs/rfc1761.html
48 */
49 public class SnoopInputStream
50 extends PacketInputStream {
51
52 /* Internal attributes */
53 private static final boolean debug = false;
54
55 private static final byte[] SNOOP_MAGIC_NUMBER = {
56 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00, 0x00, 0x00
57 };
58
59 private byte[] snoopMagicNumber = SNOOP_MAGIC_NUMBER;
60 private long snoopMajorVer;
61 private long snoopSnaplen;
62 private long snoopLinktype;
63
64 private long snoopPacketCaptureSecs;
65 private long snoopPacketCaptureNanos;
66 private long snoopPacketLength;
67 private long snoopRecordLength;
68 private long snoopCumulativeDrops;
69
70 private static String[] linkNames = {
71 "Ieee802dot3",
72 "Ieee802dot4",
73 "Ieee802dot5",
74 "Ethernet2",
75 "HDLC",
76 "CharacterSynchronous",
77 "IBMChannel",
78 "FDDI"
79 };
80
81
82 /**
83 *
84 * @param
85 * @exception
86 */
87 public SnoopInputStream(InputStream snoopFileFormatInputStream)
88 throws
89 IOException,
90 EOPacketStream,
91 StreamFormatException {
92
93 super(new BitStackInputStream(snoopFileFormatInputStream));
94 initPacketStream();
95
96 super.captureDeviceFilename = "stream: snoopFileFormat";
97 }
98
99 /**
100 * Opens up the given file and starts processing the data.
101 * @param filename Filename of the file in SNOOP fileformat to open.
102 * @exception
103 */
104 public SnoopInputStream(String filename)
105 throws
106 FileNotFoundException,
107 IOException,
108 EOPacketStream,
109 StreamFormatException {
110
111 /**
112 * Open filestream for reading and pass it on.
113 */
114 super(new BitStackInputStream(new FileInputStream(filename)));
115
116 initPacketStream();
117 super.captureDeviceFilename = "file: " + filename;
118 }
119
120 /**
121 * Since we know we are dealing with a Snoop-file formatted stream
122 * (i.e. FileInputStream("snoop-capturefile.snoop")) we initialize
123 * the ProtocolInputStream using values extracted from the snoop stream.
124 *
125 * First we extract the "snoop_file_heaer" as specified above.
126 */
127 protected void initPacketStream()
128 throws
129 IOException,
130 EOPacketStream,
131 StreamFormatException {
132
133 /**
134 * Needed to temporarily allow reading of data from the stream so
135 * we can read in the headers.
136 */
137 setForceRead(true);
138
139 try {
140 for(int i = 0; i < snoopMagicNumber.length; i ++) {
141 if(snoopMagicNumber[i] != readUnsignedByte())
142 throw new StreamFormatException(
143 "Stream in non-SNOOP format. Invalid magic number at the begining" +
144 " of the file." +
145 "(expected=" + snoopMagicNumber + ")"
146 );
147 }
148
149 snoopMajorVer = readUnsignedInt();
150 snoopLinktype = readUnsignedInt();
151 }
152 catch(EOPacket eop) {
153 // Should never happen. We are not in the packet data stream at this
154 // point.
155 throw new IOException("Packet data protection exception ocurred in initPacketStream() method. Very unexpected.");
156 }
157
158 setForceRead(false);
159
160 /**
161 * Lastly initialize the system specific things we know.
162 */
163 captureDeviceOS = System.getProperty("os.name");
164 captureDeviceOS += " " + System.getProperty("os.version");
165 captureDeviceArch += " " + System.getProperty("os.arch");
166 }
167
168 /**
169 * Read pre-packet header from stream. With basic info about the next
170 * packet to follow.
171 * this is called for every packet in the stream. Main purpose is to
172 * get packet-data length (or length of captured packet) and the
173 * capture time of the packet.
174 */
175 protected void readPacketPreHeader()
176 throws
177 IOException,
178 EOPacketStream,
179 StreamFormatException{
180
181 /**
182 * Needed to temporarily allow reading of data from the stream so
183 * we can read in the headers.
184 */
185 setForceRead(true);
186
187 try {
188
189 if(debug)
190 System.out.println("readPacketPreHeader(): "
191 + StreamUtil.toString(position()));
192
193 // packet length in the stream.
194
195 /* Included Length 32bits */
196 super.packetSnaplen = (int)readUnsignedInt();
197
198 /* Original Length 32bits */
199 snoopPacketLength = readUnsignedInt();
200 super.packetLength = (int)snoopPacketLength;
201
202
203 /* Record Length 32bits */
204 snoopRecordLength = readUnsignedInt();
205 this.recordLength = (int)snoopRecordLength - 24; // 24 is the length of the record header
206
207 System.out.println("recordLength=" + recordLength);
208
209 /* Cumulative Drops 32bits */
210 snoopCumulativeDrops = readUnsignedInt();
211
212 // secs.nanos (this is time)
213 snoopPacketCaptureSecs = (long)readUnsignedInt() * 1000;
214 super.packetCaptureTimestamp = new Timestamp(snoopPacketCaptureSecs);
215 snoopPacketCaptureNanos = readUnsignedInt() * 1000;
216 super.packetCaptureTimestamp.setNanos((int)snoopPacketCaptureNanos);
217
218 }
219 catch(IllegalArgumentException iae) {
220 /*
221 * Normally caused by Timestamp.setNanos when stream does not contain
222 * right data and we use an invalid nanos value. This exception will
223 * be caused by invalid stream format. Could be corrupt stream or
224 * incomplete file.
225 */
226 throw new StreamFormatException(
227 "Invalid data in the stream. Value is outside the valid range of " +
228 "standard primitive type timestamp.nanos. This can only be caused " +
229 " by corrupt or invalid data stream."
230 );
231 }
232 catch(EOPacket eop) {
233 /*
234 * Should never happen since we are in-between packet data and
235 * are reading packet SNOOP pre-header.
236 */
237 throw new IOException("Packet data protection exception ocurred in readPacketPreHeader() method. Very unexpected.");
238 }
239
240 setForceRead(false);
241 }
242
243 public String toString() {
244 String s = "";
245
246 if(debug) {
247 s += "snoopMagicNumber=" + snoopMagicNumber;
248 s += ", snoopMajorVer=" + snoopMajorVer;
249 s += ", snoopSnaplen=" + snoopSnaplen;
250 s += ", snoopLinktype=" + snoopLinktype;
251 s += ", timestamp=" + Long.toHexString(snoopPacketCaptureSecs);
252 s += "." + Long.toHexString(snoopPacketCaptureNanos);
253 }
254
255 return(s);
256 }
257
258 /**
259 * Returns the magic number found at the beginning of the SNOOP file.
260 */
261 public byte[] getSnoopMagicNumber() { return(snoopMagicNumber); }
262
263 /**
264 * Returns the major version number of the SNOOP file.
265 */
266 public long getSnoopMajorVer() { return(snoopMajorVer); }
267
268 /**
269 * Retuns the SNAP len of the capture file. This is the number of bytes
270 * that we saved, not captured. i.e. first 128 bytes.
271 */
272 public long snoopSnaplen() { return(snoopSnaplen); }
273
274 /**
275 * The link type of the first header in the packet. Normally this is type 2 (ethernet)
276 * but any of the others are possible as well.
277 */
278 public long snoopLinktype() { return(snoopLinktype); }
279
280 /**
281 * Number of raw number of the capture file. JNetStream library multiplies this number
282 * by factor of 1000 before returning it.
283 * <BR><BR>
284 * This value changes from packet to packet.
285 */
286 public long snoopPacketCaptureSecs() { return(snoopPacketCaptureSecs); }
287
288 /**
289 * Number of naono seconds found in the raw data file. This number is also
290 * multiplied by 1000.
291 * <BR><BR>
292 * This value changes from packet to packet.
293 */
294 public long snoopPacketCaptureNanos() { return(snoopPacketCaptureNanos); }
295
296
297
298 /**
299 * Test function for SnoopInputStream
300 * @param args command line arguments
301 */
302 public static void main(String [] args) {
303
304 String file = "abc";
305
306 if(args.length > 0)
307 file = args[0];
308
309 Hashtable perm = new Hashtable();
310 TCPAnalyzer analyzer = new TCPAnalyzer();
311
312 try {
313 SnoopInputStream snoop;
314
315 if(file.equals("-"))
316 snoop = new SnoopInputStream(System.in);
317 else
318 snoop = new SnoopInputStream(file);
319
320 System.out.println("Capture Device Address: " +
321 snoop.getCaptureDeviceAddress());
322 System.out.println("Capture Device OS: " + snoop.getCaptureDeviceOS());
323 System.out.println("Capture Device Arch: " + snoop.getCaptureDeviceArch());
324 System.out.println("Capture Device Filename: " + snoop.getCaptureDeviceFilename());
325 System.out.println("Is this capture live? " + snoop.isCaptureLive());
326 System.out.println("-------------");
327
328
329 for(int i = 0; ; i ++) {
330 try {
331
332 /*
333 * The way the example is setup there are 2 ways of running this loop.
334 *
335 * 1) is to call nextPacket() on every turn at the top. This will effectively
336 * skip any unread bytes until the end of the current packet (not the stream).
337 * After all of the data has been skipped, the PacketInputStream will read any
338 * packet data description headers expected in the stream, ie. what the length
339 * of the packet will be in the stream and the capture time of the packet.
340 *
341 * 2) Second method is to not do a nextPacket() call until the first read from
342 * the packet stream will generate a EOPacket exception. The exception is caught is
343 * the catch() statement below but still within the loop and there nextPacket() call
344 * executed and the loop starts over, but on the second time around the stream
345 * is ready for reading packet data.
346 */
347 // snoop.isReady();
348 snoop.nextPacket();
349
350
351
352 System.out.println("------------- " + (i+1) + " --------------");
353
354
355 MutablePacket pkt = new PacketImpl(perm, snoop);
356 Ethernet2 ethernet = new Ethernet2(snoop);
357 pkt.addHeader(ethernet);
358 // System.out.println(ethernet.getShortName() + ".proto=" + Integer.toHexString(ethernet.proto));
359
360
361 if(ethernet.proto == 0x800) {
362 IPv4 ip = new IPv4(snoop);
363 pkt.addHeader(ip);
364 System.out.print("[" + ip.src + "," + ip.dst + "]");
365 System.out.print("=" + ip.len);
366
367
368 snoop.push();
369
370 if(ip.proto == 6) {
371 TCP tcp = new TCP(snoop);
372 pkt.addHeader(tcp);
373 System.out.print(" " + tcp.src);
374 System.out.print("->" + tcp.dst);
375 System.out.print(" S=" + tcp.seq);
376 System.out.print(" A=" + tcp.ack);
377
378 System.out.println();
379
380 analyzer.processPacket((Packet)pkt);
381 }
382 else {
383 System.out.println("Not TCP protocol");
384 }
385
386 snoop.pop();
387 System.out.println(TCP.toString(snoop));
388 }
389
390 /*
391 * Push this position within the stream onto a stack of positions.
392 * The BitStackInputStream starts buffering from the first push.
393 * (Trust me the BitStackInputStream object is in the chain of all of
394 * InputStreams. Its internally instantiated by the PacketInputStream
395 * if it was not passed in directly.)
396 */
397 // snoop.push();
398
399 /*
400 * For kicks printout a few headers from the stream.
401 */
402 // System.out.println(Ethernet2.toString(snoop));
403 // System.out.println(IPv4.toString(snoop));
404 // System.out.println(TCP.toString(snoop));
405
406 /*
407 * Now pop the previous pushed position off of the stack in
408 * BitStackInputStream module. The stream will be rewounded back to
409 * that position and any subsequent read() calls will read from the
410 * buffer. When all of the data is read from the buffer, addtional reads
411 * will result in data from the live stream.
412 */
413 // snoop.pop();
414
415 /*
416 * Use this utility static method to print the contents of the
417 * entire packet, the same one that just printed out the headers above,
418 * but this time printout raw values in HEX. Since the data is rewound
419 * the values used by this print routine are from the stream. Because
420 * of the pop() call above, you can assume that this data is comming from
421 * a buffer, but utilizing the true stream interface.
422 */
423 // BitDataInputStream.printStream(snoop, snoop.getPacketLength());
424
425
426 /*
427 * Skip all the remaining bytes in the packet.
428 * Ofcournse snoop.skip(nBytes) is much more efficient.
429 */
430 // while(snoop.readFromPacket() != -1);
431 }
432 catch(EOPacket eop) {
433 System.out.println("Unexpected EOF Packet");
434 snoop.nextPacket();
435
436 System.out.println();
437 System.out.flush();
438 }
439 }
440 }
441 catch(StreamFormatException sfe) {
442 System.out.println(sfe);
443 System.exit(1);
444 }
445 catch(EOPacketStream eos) {
446 System.out.println();
447 System.out.println("DONE!");
448 }
449 catch(FileNotFoundException e) {
450 System.out.println(e);
451 System.exit(1);
452 }
453 catch(IOException ioe) {
454 System.out.println(ioe);
455 ioe.printStackTrace();
456 System.exit(1);
457 }
458
459 System.out.println(analyzer.toString());
460 }
461
462 } /* END OF: SnoopInputStream */