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

Quick Search    Search Deep

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