/* * 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 Snoop version 2 capture files as * defined in RFC 1761, such as the Solaris snoop utility. * * * @author Neil A. Wilson */ public class SnoopDecoder { /** * The magic bytes that must appear at the beginning of a snoop capture file. * They spell the word "snoop" followed by three null bytes. */ public static final byte[] SNOOP_HEADER_BYTES = new byte[] { 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00, 0x00, 0x00 }; /** * The data link type that indicates the capture was obtained from an IEEE * 802.3 data link. */ public static final int DATA_LINK_TYPE_IEEE_802_3 = 0; /** * The data link type that indicates the capture was obtained from an IEEE * 802.4 token bus data link. */ public static final int DATA_LINK_TYPE_IEEE_802_4 = 1; /** * The data link type that indicates the capture was obtained from an IEEE * 802.5 token ring data link. */ public static final int DATA_LINK_TYPE_IEEE_802_5 = 2; /** * The data link type that indicates the capture was obtained from an IEEE * 802.6 metro net data link. */ public static final int DATA_LINK_TYPE_IEEE_802_6 = 3; /** * The data link type that indicates the capture was obtained from an * Ethernet data link. */ public static final int DATA_LINK_TYPE_ETHERNET = 4; /** * The data link type that indicates the capture was obtained from an HDLC * data link. */ public static final int DATA_LINK_TYPE_HDLC = 5; /** * The data link type that indicates the capture was obtained from a * character synchronous data link. */ public static final int DATA_LINK_TYPE_CHARACTER_SYNCHRONOUS = 6; /** * The data link type that indicates the capture was obtained from an IBM * channel-to-channel data link. */ public static final int DATA_LINK_TYPE_CHANNEL_TO_CHANNEL = 7; /** * The data link type that indicates the capture was obtained from an FDDI * data link. */ public static final int DATA_LINK_TYPE_FDDI = 8; /** * The data link type that indicates the capture was obtained from some other * kind of data link. */ public static final int DATA_LINK_TYPE_OTHER = 9; // 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 snoop decoder that will read data from the provided input * stream. The snoop file header will be read from the input stream to * verify that it is a valid snoop v2 capture. * * @param inputStream The input stream from which the snoop 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 snoop file header. */ public SnoopDecoder(InputStream inputStream) throws IOException, SnoopException { this.inputStream = inputStream; // The first eight bytes must be "snoop" followed by three null bytes. byte[] idBytes = readBytes(inputStream, 8); if (! byteArraysAreEqual(idBytes, SNOOP_HEADER_BYTES)) { throw new SnoopException("Input does not begin with the appropriate " + "snoop header"); } // The next 4 bytes must contain an integer that specifies the snoop file // format. This decoder only handles snoop version 2 files. int snoopVersion = byteArrayToInt(readBytes(inputStream, 4)); if (snoopVersion != 2) { throw new SnoopException("Only snoop version 2 capture files are " + "supported (detected snoop version " + snoopVersion + " capture)"); } // The next four bytes specify the data link type. dataLinkType = byteArrayToInt(readBytes(inputStream, 4)); if ((dataLinkType < 0) || (dataLinkType >= 10)) { throw new SnoopException("Invalid data link type (" + dataLinkType + ')'); } } /** * 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 SnoopPacketRecord nextPacketRecord() throws IOException, SnoopException { return SnoopPacketRecord.readPacketRecord(inputStream); } /** * Reads the specified number of bytes from the given input stream. * * @param inputStream The input stream from which to read the data. * @param numBytes The number of bytes to read from the input stream. * * @return A byte array containing the requested number of bytes, or * <CODE>null</CODE> if the end of the input stream has been * reached. * * @throws IOException If a problem occurs while trying to read the * requested number of bytes. */ public static byte[] readBytes(InputStream inputStream, int numBytes) throws IOException { byte[] returnArray = new byte[numBytes]; int bytesRead = inputStream.read(returnArray); if (bytesRead < 0) { return null; } while (bytesRead < numBytes) { int moreBytesRead = inputStream.read(returnArray, bytesRead, (numBytes - bytesRead)); if (moreBytesRead < 0) { return null; } bytesRead += moreBytesRead; } return returnArray; } /** * Converts the provided byte array to an integer. * * @param byteArray The byte array containing the data to convert to an * integer. * * @return The decoded integer. */ public static int byteArrayToInt(byte[] byteArray) { return byteArrayToInt(byteArray, 0, byteArray.length); } /** * 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. * * @return The decoded integer. */ public static int byteArrayToInt(byte[] byteArray, int startPos, int length) { int value = 0x00000000; switch (length) { case 1: value |= (0x000000FF & byteArray[startPos]); break; case 2: value |= ((0x000000FF & byteArray[startPos]) << 8) | (0x000000FF & byteArray[startPos+1]); break; case 3: value |= ((0x000000FF & byteArray[startPos]) << 16) | ((0x000000FF & byteArray[startPos+1]) << 8) | (0x000000FF & byteArray[startPos+2]); break; case 4: value |= ((0x000000FF & byteArray[startPos]) << 24) | ((0x000000FF & byteArray[startPos+1]) << 16) | ((0x000000FF & byteArray[startPos+2]) << 8) | (0x000000FF & byteArray[startPos+3]); break; } return value; } /** * Converts the provided byte array to a long. * * @param byteArray The byte array containing the data to convert to a long. * * @return The decoded long. */ public static long byteArrayToLong(byte[] byteArray) { return byteArrayToLong(byteArray, 0, byteArray.length); } /** * 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. * * @return The decoded long. */ public static long byteArrayToLong(byte[] byteArray, int startPos, int length) { long value = 0x0000000000000000; 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; } return value; } /** * Indicates whether the contents of the two byte arrays are equal. * * @param array1 The first byte array to compare. * @param array2 The second byte array to compare. * * @return <CODE>true</CODE> if the byte arrays are equal, or * <CODE>false</CODE> if not. */ public static boolean byteArraysAreEqual(byte[] array1, byte[] array2) { if (array1.length != array2.length) { return false; } for (int i=0; i < array1.length; i++) { if (array1[i] != array2[i]) { return false; } } return true; } }