/*
* Sun Public License
*
* The contents of this file are subject to the Sun Public License Version
* 1.0 (the "License"). You may not use this file except in compliance with
* the License. A copy of the License is available at http://www.sun.com/
*
* The Original Code is the SLAMD Distributed Load Generation Engine.
* The Initial Developer of the Original Code is Neil A. Wilson.
* Portions created by Neil A. Wilson are Copyright (C) 2004-2010.
* Some preexisting portions Copyright (C) 2002-2006 Sun Microsystems, Inc.
* All Rights Reserved.
*
* Contributor(s): Neil A. Wilson
*/
package com.slamd.tools.ldapdecoder.snoop;
import java.io.IOException;
import java.io.InputStream;
/**
* This class defines a utility for decoding libpcap capture files such as those
* generated by tcpdump.
*
*
* @author Neil A. Wilson
*/
public class TCPDumpDecoder
{
/**
* The 32-bit value that must appear at the beginning of a pcap capture file
* using big endian byte ordering.
*/
public static final int PCAP_MAGIC_NUMBER_BIG_ENDIAN = 0xa1b2c3d4;
/**
* The 32-bit value that must appear at the beginning of a pcap capture file
* using little endian byte ordering.
*/
public static final int PCAP_MAGIC_NUMBER_LITTLE_ENDIAN = 0xd4c3b2a1;
/**
* The data link type that indicates the capture was obtained from a BSD
* loopback device.
*/
public static final int DATA_LINK_TYPE_BSD_LOOPBACK = 0;
/**
* The data link type that indicates the capture was obtained from Ethernet or
* a Linux loopback device.
*/
public static final int DATA_LINK_TYPE_ETHERNET = 1;
/**
* The data link type that indicates the capture was obtained from an 802.5
* datalink device.
*/
public static final int DATA_LINK_TYPE_802_5 = 6;
/**
* The data link type that indicates the capture was obtained from an ARCnet
* datalink device.
*/
public static final int DATA_LINK_TYPE_ARCNET = 7;
/**
* The data link type that indicates the capture was obtained from a SLIP
* datalink device.
*/
public static final int DATA_LINK_TYPE_SLIP = 8;
/**
* The data link type that indicates the capture was obtained from a PPP
* datalink device.
*/
public static final int DATA_LINK_TYPE_PPP = 9;
/**
* The data link type that indicates the capture was obtained from a FDDI
* datalink device.
*/
public static final int DATA_LINK_TYPE_FDDI = 10;
/**
* The data link type that indicates the capture was obtained from an LLC or
* SNAP-encapsulated ATM datalink device.
*/
public static final int DATA_LINK_TYPE_LLC = 100;
/**
* The data link type that indicates the capture was obtained from a raw IP
* datalink device.
*/
public static final int DATA_LINK_TYPE_RAW_IP = 101;
/**
* The data link type that indicates the capture was obtained from a BSD SLIP
* datalink device.
*/
public static final int DATA_LINK_TYPE_BSD_SLIP = 102;
/**
* The data link type that indicates the capture was obtained from a BSD PPP
* datalink device.
*/
public static final int DATA_LINK_TYPE_BSD_PPP = 103;
/**
* The data link type that indicates the capture was obtained from an HDLC
* datalink device.
*/
public static final int DATA_LINK_TYPE_HDLC = 104;
/**
* The data link type that indicates the capture was obtained from an 802.11
* datalink device.
*/
public static final int DATA_LINK_TYPE_802_11 = 105;
/**
* The data link type that indicates the capture was obtained from a Linux
* "cooked" capture datalink device.
*/
public static final int DATA_LINK_TYPE_LINUX_COOKED_CAPTURE = 113;
/**
* The data link type that indicates the capture was obtained from a LocalTalk
* datalink device.
*/
public static final int DATA_LINK_TYPE_LOCALTALK = 114;
// Indicates whether the data in this dump is big endian or little endian, as
// the specification allows for either ordering.
private boolean bigEndian;
// The input stream from which the snoop data will be read.
private InputStream inputStream;
// The datalink type from which this capture was obtained.
private int dataLinkType;
/**
* Creates a new TCPDump decoder that will read data from the provided input
* stream. The file header will be read from the input stream to verify that
* it is a valid libpcap capture.
*
* @param inputStream The input stream from which the data is to be read.
*
* @throws IOException If a problem occurs while attempting to read
* data from the provided input stream.
* @throws SnoopException If a problem occurs while attempting to decode
* the libpcap file header.
*/
public TCPDumpDecoder(InputStream inputStream)
throws IOException, SnoopException
{
this.inputStream = inputStream;
// The first 32-bit word must be the pcap magic number.
int magicNumber =
SnoopDecoder.byteArrayToInt(SnoopDecoder.readBytes(inputStream, 4));
if (magicNumber == PCAP_MAGIC_NUMBER_BIG_ENDIAN)
{
bigEndian = true;
}
else if (magicNumber == PCAP_MAGIC_NUMBER_LITTLE_ENDIAN)
{
bigEndian = false;
}
else
{
throw new SnoopException("Input does not begin with the appropriate " +
"libpcap magic number");
}
// The next 16-bit word must be the major version, and it must be 2.
int majorVersion = byteArrayToInt(SnoopDecoder.readBytes(inputStream, 2),
bigEndian);
if (majorVersion != 2)
{
throw new SnoopException("Only libpcap capture files with a major " +
"version of 2 are supported (detected major " +
"version " + majorVersion + ')');
}
// The next 16-bit word must be the minor version. We don't really care
// what it is, although it should be 4.
int minorVersion = byteArrayToInt(SnoopDecoder.readBytes(inputStream, 2),
bigEndian);
// The next two 32-bit words are time zone and time stamp data which are
// actually not used, so we'll ignore them.
SnoopDecoder.readBytes(inputStream, 8);
// The next 32-bit value is the snapshot length. We just want to make sure
// that it is nonzero for now.
int snapshotLength = byteArrayToInt(SnoopDecoder.readBytes(inputStream, 4),
bigEndian);
if (snapshotLength == 0)
{
throw new SnoopException("Snapshot length must be nonzero");
}
// The last 32-bit value in the header is the datalink type.
dataLinkType = byteArrayToInt(SnoopDecoder.readBytes(inputStream, 4),
bigEndian);
}
/**
* Retrieves the data link type for this snoop capture.
*
* @return The data link type for this snoop capture.
*/
public int getDataLinkType()
{
return dataLinkType;
}
/**
* Reads the next packet record from the input stream.
*
* @return The next packet record from the input stream, or
* <CODE>null</CODE> if the end of the stream has been reached.
*
* @throws IOException If a problem occurs while reading data from the
* input stream.
* @throws SnoopException If a problem occurs while trying to decode the
* packet record.
*/
public TCPDumpPacketRecord nextPacketRecord()
throws IOException, SnoopException
{
return TCPDumpPacketRecord.readPacketRecord(inputStream, bigEndian);
}
/**
* Converts the provided byte array to an integer.
*
* @param byteArray The byte array containing the data to convert to an
* integer.
* @param bigEndian Indicates whether the data in the byte array uses big
* endian or little endian decoding.
*
* @return The decoded integer.
*/
public static int byteArrayToInt(byte[] byteArray, boolean bigEndian)
{
return byteArrayToInt(byteArray, 0, byteArray.length, bigEndian);
}
/**
* Converts the specified data from the provided byte array to an integer.
*
* @param byteArray The byte array containing the data to convert to an
* integer.
* @param startPos The position in the byte array to start decoding.
* @param length The number of bytes to decode.
* @param bigEndian Indicates whether the data in the byte array uses big
* endian or little endian decoding.
*
* @return The decoded integer.
*/
public static int byteArrayToInt(byte[] byteArray, int startPos, int length,
boolean bigEndian)
{
int value = 0x00000000;
if (bigEndian)
{
switch (length)
{
case 1: value |= (0xFF & byteArray[startPos]);
break;
case 2: value |= ((0xFF & byteArray[startPos]) << 8) |
(0xFF & byteArray[startPos+1]);
break;
case 3: value |= ((0xFF & byteArray[startPos]) << 16) |
((0xFF & byteArray[startPos+1]) << 8) |
(0xFF & byteArray[startPos+2]);
break;
case 4: value |= ((0xFF & byteArray[startPos]) << 24) |
((0xFF & byteArray[startPos+1]) << 16) |
((0xFF & byteArray[startPos+2]) << 8) |
(0xFF & byteArray[startPos+3]);
break;
}
}
else
{
switch (length)
{
case 1: value |= (0xFF & byteArray[startPos]);
break;
case 2: value |= ((0xFF & byteArray[startPos+1]) << 8) |
(0xFF & byteArray[startPos]);
break;
case 3: value |= ((0xFF & byteArray[startPos+2]) << 16) |
((0xFF & byteArray[startPos+1]) << 8) |
(0xFF & byteArray[startPos]);
break;
case 4: value |= ((0xFF & byteArray[startPos+3]) << 24) |
((0xFF & byteArray[startPos+2]) << 16) |
((0xFF & byteArray[startPos+1]) << 8) |
(0xFF & byteArray[startPos]);
break;
}
}
return value;
}
/**
* Converts the provided byte array to a long.
*
* @param byteArray The byte array containing the data to convert to a long.
* @param bigEndian Indicates whether the data should be decoded in big
* endian or little endian form.
*
* @return The decoded long.
*/
public static long byteArrayToLong(byte[] byteArray, boolean bigEndian)
{
return byteArrayToLong(byteArray, 0, byteArray.length, bigEndian);
}
/**
* Converts the specified data from the provided byte array to a long.
*
* @param byteArray The byte array containing the data to convert to a long.
* @param startPos The position in the byte array to start decoding.
* @param length The number of bytes to decode.
* @param bigEndian Indicates whether the data should be decoded in big
* endian or little endian form.
*
* @return The decoded long.
*/
public static long byteArrayToLong(byte[] byteArray, int startPos, int length,
boolean bigEndian)
{
long value = 0x0000000000000000;
if (bigEndian)
{
switch (length)
{
case 1: value |= (0xFFL & byteArray[startPos]);
break;
case 2: value |= ((0xFFL & byteArray[startPos]) << 8) |
(0xFFL & byteArray[startPos+1]);
break;
case 3: value |= ((0xFFL & byteArray[startPos]) << 16) |
((0xFFL & byteArray[startPos+1]) << 8) |
(0xFFL & byteArray[startPos+2]);
break;
case 4: value |= ((0xFFL & byteArray[startPos]) << 24) |
((0xFFL & byteArray[startPos+1]) << 16) |
((0xFFL & byteArray[startPos+2]) << 8) |
(0xFFL & byteArray[startPos+3]);
break;
case 5: value |= ((0xFFL & byteArray[startPos]) << 32) |
((0xFFL & byteArray[startPos+1]) << 24) |
((0xFFL & byteArray[startPos+2]) << 16) |
((0xFFL & byteArray[startPos+3]) << 8) |
(0xFFL & byteArray[startPos+4]);
break;
case 6: value |= ((0xFFL & byteArray[startPos]) << 40) |
((0xFFL & byteArray[startPos+1]) << 32) |
((0xFFL & byteArray[startPos+2]) << 24) |
((0xFFL & byteArray[startPos+3]) << 16) |
((0xFFL & byteArray[startPos+4]) << 8) |
(0xFFL & byteArray[startPos+5]);
break;
case 7: value |= ((0xFFL & byteArray[startPos]) << 48) |
((0xFFL & byteArray[startPos+1]) << 40) |
((0xFFL & byteArray[startPos+2]) << 32) |
((0xFFL & byteArray[startPos+3]) << 24) |
((0xFFL & byteArray[startPos+4]) << 16) |
((0xFFL & byteArray[startPos+5]) << 8) |
(0xFFL & byteArray[startPos+6]);
break;
case 8: value |= ((0xFFL & byteArray[startPos]) << 56) |
((0xFFL & byteArray[startPos+1]) << 48) |
((0xFFL & byteArray[startPos+2]) << 40) |
((0xFFL & byteArray[startPos+3]) << 32) |
((0xFFL & byteArray[startPos+4]) << 24) |
((0xFFL & byteArray[startPos+5]) << 16) |
((0xFFL & byteArray[startPos+6]) << 8) |
(0xFFL & byteArray[startPos+7]);
break;
}
}
else
{
switch (length)
{
case 1: value |= (0xFFL & byteArray[startPos]);
break;
case 2: value |= ((0xFFL & byteArray[startPos+1]) << 8) |
(0xFFL & byteArray[startPos]);
break;
case 3: value |= ((0xFFL & byteArray[startPos+2]) << 16) |
((0xFFL & byteArray[startPos+1]) << 8) |
(0xFFL & byteArray[startPos]);
break;
case 4: value |= ((0xFFL & byteArray[startPos+3]) << 24) |
((0xFFL & byteArray[startPos+2]) << 16) |
((0xFFL & byteArray[startPos+1]) << 8) |
(0xFFL & byteArray[startPos]);
break;
case 5: value |= ((0xFFL & byteArray[startPos+4]) << 32) |
((0xFFL & byteArray[startPos+3]) << 24) |
((0xFFL & byteArray[startPos+2]) << 16) |
((0xFFL & byteArray[startPos+1]) << 8) |
(0xFFL & byteArray[startPos]);
break;
case 6: value |= ((0xFFL & byteArray[startPos+5]) << 40) |
((0xFFL & byteArray[startPos+4]) << 32) |
((0xFFL & byteArray[startPos+3]) << 24) |
((0xFFL & byteArray[startPos+2]) << 16) |
((0xFFL & byteArray[startPos+1]) << 8) |
(0xFFL & byteArray[startPos]);
break;
case 7: value |= ((0xFFL & byteArray[startPos+6]) << 48) |
((0xFFL & byteArray[startPos+5]) << 40) |
((0xFFL & byteArray[startPos+4]) << 32) |
((0xFFL & byteArray[startPos+3]) << 24) |
((0xFFL & byteArray[startPos+2]) << 16) |
((0xFFL & byteArray[startPos+1]) << 8) |
(0xFFL & byteArray[startPos]);
break;
case 8: value |= ((0xFFL & byteArray[startPos+7]) << 56) |
((0xFFL & byteArray[startPos+6]) << 48) |
((0xFFL & byteArray[startPos+5]) << 40) |
((0xFFL & byteArray[startPos+4]) << 32) |
((0xFFL & byteArray[startPos+3]) << 24) |
((0xFFL & byteArray[startPos+2]) << 16) |
((0xFFL & byteArray[startPos+1]) << 8) |
(0xFFL & byteArray[startPos]);
break;
}
}
return value;
}
}