Source code: com/voytechs/jnetstream/io/PacketInputStream.java
1 /*
2 * File: PacketInputStream.java
3 * Auth: Mark Bednarczyk
4 * Date: 2003-06-30
5 * Id: $Id: PacketInputStream.java,v 1.1.1.1 2003/09/22 16:32:07 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: PacketInputStream.java,v $
24 * Revision 1.1.1.1 2003/09/22 16:32:07 voytechs
25 * Initial import.
26 *
27 * Revision 1.1 2003/07/02 19:41:43 markbe
28 * Initial revision
29 *
30 */
31 package com.voytechs.jnetstream.io;
32
33 import com.voytechs.jnetstream.primitive.address.IpAddress;
34
35 import java.lang.*;
36 import java.util.*;
37 import java.io.*;
38 import java.net.UnknownHostException;
39 import java.sql.Timestamp;
40
41 /**
42 * Stream object that reads either an InputStream of bytes and allows
43 * access to the byte stream with the following features:<BR>
44 * 1) data can be read in either bytes or individual bits.<BR>
45 * 2) position within the stream can be pushed on to a stack<BR>
46 * 3) position can be poped off of the stack.<BR>
47 * 4) a packet structure is imposed on the stream so that you can query or
48 * be notified when the end of an individual packet byte stream is over
49 * and when the next packet byte stream is beginning.<BR>
50 * 5) data can be read in any binary format (Big Endian or Little Endian)<BR>
51 * 6) Packet capture information is accessable, such as:<BR>
52 * a) IP address of the capture device.<BR>
53 * b) interface or filename the packet was captured on.<BR>
54 * c) capture time of the packet<BR>
55 * d) length of the entire packet in bytes<BR>
56 * e) OS name of the capture device<BR>
57 * d) OS version of the capture device<BR>
58 */
59 public class PacketInputStream
60 extends ProtocolDataInputStream {
61
62 /* Internal attributes */
63 private static final boolean debug = false;
64
65 /**
66 * Reference to stack input so we can push() and pop() positions
67 * witin.
68 */
69 public BitStackInputStream stackIn;
70
71 /**
72 * Flag indicates that we as a stream are ready to process
73 * the packet data portion of the input stream. First a call
74 * to nextPacket() must be made, otherwise we won't be able to
75 * read the packet specific pre-header.
76 */
77 private boolean forceRead = false;
78
79 /**
80 * Standard fields part of the jnetstream between PacketInput and
81 * PacketOutput streams.
82 */
83
84 /**
85 * Length of the captured packet.
86 */
87 protected long packetLength = 0;
88 protected long packetSnaplen = 0;
89 protected long packetStart = 0;
90 protected long packetEnd = 0;
91
92 private long recordHeaderLength = 0;
93 protected long recordLength = 0;
94 protected long recordStart = 0;
95 protected long recordEnd = 0;
96
97 /**
98 * Capture time of the packet.
99 */
100 protected Timestamp packetCaptureTimestamp = new Timestamp(0);
101
102
103 /**
104 * Capture device IP address.
105 * Initialize to default in the constructor.
106 */
107 protected IpAddress captureDeviceIp = null;
108
109 /**
110 * Capture device OS.
111 */
112 protected String captureDeviceOS = "";
113
114 /**
115 * Capture device hardware architecture.
116 */
117 protected String captureDeviceArch = "";
118
119 /**
120 * Name of the file or interface the capture
121 * occured. If known.
122 */
123 protected String captureDeviceFilename = "";
124
125
126 /**
127 * Flag indicating if the stream is comming from
128 * a live capture or a pre-captured file.
129 */
130 protected boolean captureLive = false;
131
132
133 protected String linkType = "";
134
135
136 /**
137 *
138 * @param
139 * @exception
140 */
141 public PacketInputStream(BitStackInputStream inputStream)
142 throws
143 IOException,
144 EOPacketStream,
145 StreamFormatException {
146
147 /**
148 * StackInputStream interface needed inorder to properly execute
149 * push() and pop() methods on the stream. Also the exact position
150 * within the overall stream is needed via the position() method
151 * inorder to determine boundaries of each packet-data so that
152 * the jnetstream of processing one packet at a time can be enforced.
153 * before the next packet can be processed the nextPacket() method
154 * must be called. Otherwise if the end of packet-data is reached
155 * before nextPacket() is called an EOPacket exception is thrown.
156 */
157 super(inputStream);
158
159 stackIn = inputStream;
160
161 try { captureDeviceIp = new IpAddress("127.0.0.1"); }
162 catch(UnknownHostException u) { }
163
164
165 }
166
167 /*
168 ****** DEFINE accessor methods for stream and packet info ******
169 */
170
171
172
173
174 /**
175 * Returns the name of the first header in the packet.
176 */
177 public String getLinkType() {
178 return(linkType);
179 }
180
181 /**
182 * Sets the First Header in the packet, usually a Data link layer,
183 * but not neccessarily.
184 *
185 * @param linkType First Header in the packet, usually a Data Link frame type.
186 */
187 protected void setLinkType(String linkType) {
188 this.linkType = linkType;
189 }
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205 /**
206 * Returns the start of the packet record in the stream including all the headers.
207 * This includes the capture file's packet pre-header plus the included packet as well.
208 */
209 protected long getRecordStart() {
210 return(recordStart);
211 }
212
213
214 /**
215 * This is an unusual call, since record start and record end
216 * are normally computed by call to nextPacket() method which advances
217 * the stream to the beginning of the next record and after reading of the
218 * pre-header to the beginning of the packet in the stream.
219 *
220 * This method may be used to rewiding the position in the stream to a previous
221 * record's start position.
222 *
223 * This method also resets the record length and record end properties. So they
224 * should be set appriopriately after words.
225 *
226 * @param start start position in the stream.
227 */
228 protected void setRecordStart(long start) {
229
230 recordStart = start;
231 setRecordLength(0); // Also sets the record end property
232
233 }
234
235 /**
236 * Returns the length of the packet in bytes. This is the
237 * Snapped length value. Where the packet got chopped off.
238 */
239 public long getRecordLength() {
240 return(recordLength);
241 }
242
243 /**
244 * Returns the position of the record end in the stream.
245 */
246 public long getRecordEnd() {
247 return(recordEnd);
248 }
249
250
251 /*
252 * Sets the record end property. This should only be done by PacketInputStream object
253 * and thus is set private. Subclasses should use the setRecordLength() property
254 * to set the record end property.
255 *
256 * @param end end position of the record in the stream.
257 */
258 private void setRecordEnd(long end) {
259 recordEnd = end;
260 }
261
262 /**
263 * Sets the trunkated packet length during the capture. If the packet
264 * was trunkated at the time of the capture, this length will be less
265 * then the length returned by getPacketLength(). Otherwise they
266 * should be the same.
267 *
268 * The method also sets the record end property using the setRecordEnd() method.
269 *
270 */
271 protected void setRecordLength(long length) {
272 recordLength = length;
273
274 setRecordEnd(getRecordStart() + recordLength);
275 }
276
277
278 /**
279 * Returns the length of the record header excluding the rest of the packet.
280 * @return length of the header portion of the record.
281 */
282 protected long getRecordHeaderLength() {
283 return(recordHeaderLength);
284 }
285
286
287 /**
288 * Sets the length of the record header. This method also advances the
289 * packet start position and end positions by this amount starting at record start.
290 * The packet end position could end up different from record end properties if there
291 * is a post header in the capture file or stream.
292 */
293 private void setRecordHeaderLength(long length) {
294 recordHeaderLength = length;
295
296 setPacketStart(getRecordStart() + getRecordHeaderLength());
297 setPacketEnd(getPacketStart() + getPacketSnaplen());
298 }
299
300
301
302
303
304
305
306
307
308 /**
309 * Returns the length of the packet in bytes as seen on the wire.
310 */
311 public long getPacketLength() {
312 return(packetLength);
313 }
314
315 /**
316 * Sets the original packet length as seen on the network wire.
317 *
318 * @param length length in bytes of packet as seen on the wire.
319 */
320 protected void setPacketLength(long length) {
321 this.packetLength = length;
322 }
323
324 /**
325 * Returns the length of the packet in bytes. This is the
326 * Snapped length value. Where the packet got chopped off.
327 */
328 public long getPacketSnaplen() {
329 return(packetSnaplen);
330 }
331
332
333 /**
334 * Sets the trunkated packet length during the capture. If the packet
335 * was trunkated at the time of the capture, this length will be less
336 * then the length returned by getPacketLength(). Otherwise they
337 * should be the same.
338 *
339 * This method also sets the packet end property using the setPacketEnd() method.
340 * Snaplen is used to determine the packet end in the stream since getPacketLength()
341 * may not be what is actually in the capture stream due to trunkation of the packet.
342 *
343 * @param length length of the packet record in the capture file or stream.
344 */
345 protected void setPacketSnaplen(long length) {
346 packetSnaplen = length;
347 }
348
349
350 /**
351 * Returns the beginning position of the packet in bytes.
352 */
353 public long getPacketStart() {
354 return(packetStart);
355 }
356
357
358 /**
359 * Sets the start of the packet in the stream.
360 * This is used to calculate the packet end so that packet boundary
361 * conditions can be enforced.
362 *
363 * The call to this method resets the packet length and packet end properties.
364 * They should be set by a call to setPacketSnaplen() method which will set them
365 * appropriately.
366 */
367 public void setPacketStart(long start) {
368 packetStart = start;
369 }
370
371
372 /**
373 * Returns the ending position of the packet in bytes.
374 */
375 public long getPacketEnd() {
376 return(packetEnd);
377 }
378
379 /*
380 * The method setPacketEnd() this method should not be normally called because this
381 * property should not be set manually but automatically by the setPacketLength() method.
382 */
383 private void setPacketEnd(long end) {
384 packetEnd = end;
385 }
386
387 /**
388 * Returns the length of the packet in bytes.
389 */
390 public int getPacketLengthRemaining() {
391 return((int)(packetEnd - stackIn.position()));
392 }
393
394
395
396
397
398
399
400
401
402
403 /**
404 * returns the time the packet was captured.
405 */
406 public Timestamp getCaptureTimestamp() {
407 return(packetCaptureTimestamp);
408 }
409
410 /**
411 * Sets the current capture timestamp. Timestamp is the time at which
412 * the packet was captured off of the wire.
413 */
414 protected void setCaptureTimestamp(Timestamp captureTimestamp) {
415 this.packetCaptureTimestamp = captureTimestamp;
416 }
417
418 /**
419 * Return IP address of the device the stream originated on
420 * or if known where the capture occured.
421 */
422 public IpAddress getCaptureDeviceAddress() {
423 return(captureDeviceIp);
424 }
425
426 /**
427 * Return OS name of the device the stream originated or
428 * if know where the capture occured.
429 */
430 public String getCaptureDeviceOS() {
431 return(captureDeviceOS);
432 }
433
434 /**
435 * Return OS name of the device the stream originated or
436 * if know where the capture occured.
437 */
438 public String getCaptureDeviceArch() {
439 return(captureDeviceArch);
440 }
441
442 /**
443 * Return filename name or interface name of the source of this
444 * data.
445 */
446 public String getCaptureDeviceFilename() {
447 return(captureDeviceFilename);
448 }
449
450 /**
451 * Return Ip address of the source of this data.
452 * data.
453 */
454 public IpAddress getCaptureDeviceIp() {
455 return(captureDeviceIp);
456 }
457
458
459
460
461
462
463
464
465 /**
466 * Idicates wheather this capture stream is from a live source
467 * or offline or file data. If data was captured previously and saved
468 * then false will be returned. Reading from a system interface would
469 * return "true".
470 */
471 public boolean isCaptureLive() {
472 return(captureLive);
473 }
474
475
476
477 /*
478 ****** DEFINE PacketInputStream methods ******
479 */
480
481
482
483 /**
484 * Read stream ID data. This is only called once during the stream
485 * initialization and no more.
486 */
487 protected void initPacketStream()
488 throws
489 IOException,
490 EOPacketStream,
491 StreamFormatException {
492
493 /**
494 * Needed to temporarily allow reading of data from the stream so
495 * we can read in the headers.
496 */
497 setForceRead(true);
498 setForceRead(false);
499 }
500
501 /**
502 * Read pre-packet header from stream. With basic info about the next
503 * packet to follow.
504 * this is called for every packet in the stream. Main purpose is to
505 * get packet-data length (or length of captured packet) and the
506 * capture time of the packet.
507 */
508 protected void readPacketPreHeader()
509 throws
510 IOException,
511 EOPacketStream,
512 StreamFormatException {
513
514 /**
515 * Needed to temporarily allow reading of data from the stream so
516 * we can read in the headers.
517 */
518 // setForceRead(true);
519 // setForceRead(false);
520 }
521
522 /**
523 * Pushes the current position in the stream onto a stack. The position
524 * can be restored and stream rewound by using pop().
525 */
526 public void push() {
527 stackIn.push(bitsLeft);
528 }
529
530 /**
531 * Pushes the current position in the stream onto a stack and mark's it with
532 * a name.
533 */
534 public void push(String markName) {
535 stackIn.push(bitsLeft, markName);
536 }
537
538 public boolean gotoMark(String markName) {
539 return(stackIn.gotoMark(markName));
540 }
541
542 /**
543 * Pops previously pushed position off of a stack and rewids the
544 * stream.
545 */
546 public void pop() {
547 bitsLeft = stackIn.popOnBitBoundary();
548 }
549
550 /**
551 * Clears previously pushed position off of a stack and without rewinding the
552 * stream.
553 */
554 public void clear() {
555
556 try {
557
558 stackIn.clear();
559
560 }
561 catch(IOException ioe) {
562
563 ioe.printStackTrace();
564
565 }
566 }
567
568 /**
569 * Allow reads by force. Normally read() method using the ProtocolInputStream
570 * object is reserved for reading packet data. In certain cases in order
571 * to proccess the the ProtocolStream jnetstream between ProtocolInputStream
572 * and ProtocolOutputStream it is neccessary to allow the read() operation
573 * to be performed. This is very implementation specific and is not
574 * accessable as a public method.
575 * Remember to always leave this at false when not required any more. If
576 * you leave it true all the time then reading of just the packet data using
577 * read() method won't be enforced and would cause all kinds of
578 * nasty side effects. Such as EOPacket exception not thrown.
579 *
580 * Remember to catch IOException and reset this flag if it has been
581 * set to true otherwise an exception thrown might cause this flag
582 * to be "true" and break the function of read() method.
583 *
584 */
585 protected void setForceRead(boolean status) {
586 forceRead = status;
587 }
588
589 protected boolean hasForceRead() {
590 return(forceRead);
591 }
592
593 /**
594 * Read packet data from the stream.
595 * Only allow a read after nextPacket() has been called and
596 * there is still data in the packet inputstream to read. A EOPacket
597 * exception is thrown and can be used as an indication that the end of
598 * current packet-data has been reached. Even though the packet jnetstream
599 * header may indicate that there should be more data to read,
600 *
601 * @return A byte of data from actual packet data.
602 *
603 * @exception IOException Any problems with the stream IO.
604 *
605 * @exception EOPacket Thrown if end of packet data has been reached
606 * within the current stream. nextPacket() method must be called before
607 * the next packet data can be read.
608 */
609 public int read()
610 throws IOException{
611
612 return(super.read());
613 }
614
615
616 /**
617 * Prepares the stream for the next packet in the stream. This method must
618 * be called the first time stream is used and after all of the data
619 * in current packet being processed is exhausted. The method can also
620 * be called in the middle of data processing before entire contents of
621 * the packet-data are read. The underlying stream will be advanced to
622 * the beginning of the next packet and all unread data will be skipped.
623 *
624 * The read() method can not be called the nextPacket() method was used
625 * to prapare and advance the stream to beginning of packet-data.
626 * A EOPacket exception will be thrown by the read() method without
627 * the nextPacket() call first.
628 *
629 *
630 */
631 public void nextPacket()
632 throws
633 IOException,
634 EOPacketStream,
635 StreamFormatException {
636
637 goToEndOfRecord();
638
639 /*
640 * Set the record start property.
641 */
642 recordStart = stackIn.position();
643
644 try {
645 if(debug)
646 System.out.println(
647 "nextPacket(): pre-firstByte="
648 + StreamUtil.toString(stackIn.position()));
649
650 /*
651 * Reset these properties to make sure that we do not, by accident reuse
652 * the old values to calculate positions of packet in the next record.
653 */
654 setPacketLength(0);
655 setPacketSnaplen(0);
656 setRecordLength(0);
657
658 readPacketPreHeader();
659
660 setRecordHeaderLength(stackIn.position() - recordStart); // Also adjusts the packet start and end positions
661
662 if(debug)
663 System.out.println(
664 "nextPacket(): post-firstByte="
665 + StreamUtil.toString(stackIn.position()));
666 }
667 catch(IOException ioe) {
668 /**
669 * Make sure that forceRead flag is reset after an exception.
670 */
671 setForceRead(false);
672 throw ioe;
673 }
674 }
675
676 /**
677 * A special method that forwards the current position in the stream
678 * to the end of the packet. After this method call it will be possible
679 * to start on the next packet or if pop() occured this method can be
680 * called again.
681 * This method is not public accessible. nextPacket() method should be used
682 * to advance to the next packet even if there is still data in existing
683 * packet-data stream.
684 */
685 protected void goToEndOfRecord()
686 throws IOException, StreamFormatException {
687
688 /**
689 * Advance the stream to the end of current packet.
690 */
691
692 long sk = getRecordEnd() - stackIn.position();
693 if(sk < 0)
694 throw new StreamFormatException("End of record is smaller then position in the stream. " +
695 "This is an error! Must be invalid data in the stream.");
696
697 stackIn.skip(sk);
698
699 /**
700 * Important to clear the bitsLeft counter.
701 * This makes sure that the end of packet alignment is correct even
702 * if the underlying protocol messed up and after the decode the
703 * bitsLeft wasn't left aligned at 0 bits left.
704 */
705 bitsLeft = 0;
706 }
707
708 /**
709 * Skip ahead.
710 */
711 public long skip(long bytes)
712 throws IOException {
713
714 bitsLeft = 0; // When skipping bytes, the bitsLeft has to be reset.
715
716 return(stackIn.skip(bytes));
717 }
718
719 /**
720 * protected accessible method that reports the position within
721 * the overall stream.
722 */
723 public long position() {
724 return(stackIn.position());
725 }
726
727 public int bitsLeft() {
728 return(bitsLeft);
729 }
730
731
732 /**
733 * Returns a flag indicating if stream is ready for packet processing.
734 * @return if true you can call the read() method.
735 */
736 public boolean isPacketReady() {
737
738 if(hasForceRead())
739 return(true); // Ignores boundaries
740
741 if( (stackIn.position() >= getPacketStart() && stackIn.position() < getPacketEnd()))
742 return(true);
743 else
744 return(false);
745 }
746
747 /**
748 * Returns a flag indicating if stream is ready for packet processing.
749 * @return if true you can call the read() method.
750 * @exception EOPacket is trown if stream is not ready.
751 */
752 protected boolean isReady()
753 throws EOPacket {
754
755 return(isReady(0));
756 }
757 /**
758 * Returns a flag indicating if stream is ready for packet processing.
759 * @return if true you can call the read() method.
760 * @exception EOPacket is trown if stream is not ready.
761 *
762 * @return This method always returns true. If boundary check fails an exception is thrown instead of returning false.
763 */
764 protected boolean isReady(int len)
765 throws EOPacket {
766
767 if(hasForceRead())
768 return(true); // Ignores boundaries
769
770 /**
771 * First check the bounds on the current position if its within the proper boundaries.
772 */
773 if( stackIn.position() < getPacketStart() )
774 throw new EOPacket("Need to call nextPacket() method! (position=" + stackIn.position() +
775 " len=" + len +
776 " packet end=" + getPacketEnd());
777
778 if((stackIn.position() + len) > getPacketEnd())
779 throw new EOPacket("Need to call nextPacket() method! (position=" + stackIn.position() +
780 " len=" + len +
781 " packet end=" + getPacketEnd());
782
783 return(true);
784 }
785
786 /**
787 * Test function for PacketInputStream
788 * @param args command line arguments
789 */
790 public static void main(String [] args) {
791
792 StackInputStream sis = null;
793 try {
794 sis = new StackInputStream(new FileInputStream(args[0]));
795
796
797 sis.push();
798 printStream(sis, 16);
799
800 }
801 catch(FileNotFoundException foe) {
802 System.out.println(foe);
803 System.exit(1);
804 }
805 catch(IOException ioe) {
806 System.out.println(ioe);
807 System.exit(1);
808 }
809
810
811 }
812
813 public static void printStream(InputStream in, int printCount)
814 throws IOException {
815
816 int count = 1;
817 int b = 0;
818 while( (b = in.read()) != -1 && printCount != 0) {
819 System.out.print(hex(b));
820
821 if(count != 0 && (count % 2) == 0)
822 System.out.print(" ");
823
824 if(count != 0 && (count % 16) == 0)
825 System.out.println("");
826 count ++;
827
828 printCount --;
829 }
830
831 System.out.println("");
832 }
833
834 public static String hex(int b) {
835 String s = Integer.toHexString(b);
836
837 if(s.length() == 1)
838 s = "0" + s;
839
840 return(s);
841 }
842
843 } /* END OF: PacketInputStream */