All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.pcap4j.packet.AbstractPacket Maven / Gradle / Ivy

There is a newer version: 2.0.0-alpha.6
Show newest version
/*_##########################################################################
  _##
  _##  Copyright (C) 2011-2019 Pcap4J.org
  _##
  _##########################################################################
*/

package org.pcap4j.packet;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.pcap4j.util.ByteArrays;
import org.pcap4j.util.LazyValue;
import org.pcap4j.util.LazyValue.BuildValueCommand;

/**
 * Abstract immutable packet class. If you use {@link
 * org.pcap4j.packet.factory.propertiesbased.PropertiesBasedPacketFactory
 * PropertiesBasedPacketFactory}, this subclass must implement the following method: {@code public
 * static Packet newPacket(byte[] rawData, int offset, int length) throws IllegalRawDataException}
 *
 * @author Kaito Yamada
 * @since pcap4j 0.9.1
 */
public abstract class AbstractPacket implements Packet {

  /** */
  private static final long serialVersionUID = -3016622134481071576L;

  private final LazyValue lengthCache;
  private final LazyValue rawDataCache;
  private final LazyValue hexStringCache;
  private final LazyValue stringCache;
  private final LazyValue hashCodeCache;

  /** */
  public AbstractPacket() {
    this.lengthCache =
        new LazyValue(
            new BuildValueCommand() {
              @Override
              public Integer buildValue() {
                return calcLength();
              }
            });
    this.rawDataCache =
        new LazyValue(
            new BuildValueCommand() {
              @Override
              public byte[] buildValue() {
                return buildRawData();
              }
            });
    this.hexStringCache =
        new LazyValue(
            new BuildValueCommand() {
              @Override
              public String buildValue() {
                return buildHexString();
              }
            });
    this.stringCache =
        new LazyValue(
            new BuildValueCommand() {
              @Override
              public String buildValue() {
                return buildString();
              }
            });
    this.hashCodeCache =
        new LazyValue(
            new BuildValueCommand() {
              @Override
              public Integer buildValue() {
                return calcHashCode();
              }
            });
  }

  /**
   * Returns the Header object representing this packet's header. This subclass have to override
   * this method if the packet represented by the subclass has a header.
   */
  @Override
  public Header getHeader() {
    return null;
  }

  /**
   * Returns the Packet object representing this packet's payload. This subclass have to override
   * this method if the packet represented by the subclass has a payload.
   */
  @Override
  public Packet getPayload() {
    return null;
  }

  /**
   * This method calculates the value {@link #length length()} will return by adding up the header
   * length and the payload length. If you write this subclass which represents a packet with extra
   * parts (e.g. a trailer), you need to override this method.
   *
   * @return a calculated length
   */
  protected int calcLength() {
    int length = 0;

    if (getHeader() != null) {
      length += getHeader().length();
    }
    if (getPayload() != null) {
      length += getPayload().length();
    }

    return length;
  }

  /**
   * Returns the packet length in bytes. This method calls {@link #calcLength calcLength()} and
   * caches the return value when it is called for the first time, and then, this method returns the
   * cached value from the second time.
   */
  @Override
  public int length() {
    return lengthCache.getValue();
  }

  /**
   * This method builds the value {@link #getRawData getRawData()} will return by concatenating the
   * header's raw data and the payload's raw data. If you write this subclass which represents a
   * packet with extra parts (e.g. a trailer), you need to override this method.
   *
   * @return a raw data built
   */
  protected byte[] buildRawData() {
    byte[] rd = new byte[length()];
    Header header = getHeader();
    Packet payload = getPayload();

    int dstPos = 0;
    if (header != null) {
      System.arraycopy(getHeader().getRawData(), 0, rd, 0, header.length());
      dstPos += header.length();
    }
    if (payload != null) {
      System.arraycopy(getPayload().getRawData(), 0, rd, dstPos, payload.length());
      dstPos += payload.length();
    }

    return rd;
  }

  /**
   * Returns this packet's raw data. This method calls {@link #buildRawData buildRawData()} and
   * caches the return value when it is called for the first time, and then, this method returns the
   * cached value from the second time. More correctly, this method returns a copy of the cached
   * value, so that the cache can't be changed.
   */
  @Override
  public byte[] getRawData() {
    byte[] rawData = rawDataCache.getValue();

    byte[] copy = new byte[rawData.length];
    System.arraycopy(rawData, 0, copy, 0, copy.length);
    return copy;
  }

  @Override
  public Iterator iterator() {
    return new PacketIterator(this);
  }

  @Override
  public  T get(Class clazz) {
    for (Packet p : this) {
      if (clazz.isInstance(p)) {
        return clazz.cast(p);
      }
    }
    return null;
  }

  @Override
  public Packet getOuterOf(Class clazz) {
    for (Packet p : this) {
      if (clazz.isInstance(p.getPayload())) {
        return p;
      }
    }
    return null;
  }

  @Override
  public  boolean contains(Class clazz) {
    return get(clazz) != null;
  }

  @Override
  public abstract Builder getBuilder();

  /**
   * This method builds the value {@link #toHexString toHexString()} will return using the return
   * value of {@link #getRawData getRawData()}. Each octet in this return value is separated by a
   * white space. (e.g. 00 01 02 03 aa bb cc)
   *
   * @return a hex string representation of this object
   */
  protected String buildHexString() {
    return ByteArrays.toHexString(getRawData(), " ");
  }

  /**
   * Returns the hex string representation of this object. This method calls {@link #buildHexString
   * buildHexString()} and caches the return value when it is called for the first time, and then,
   * this method returns the cached value from the second time.
   *
   * @return a hex string representation of this object
   */
  public String toHexString() {
    return hexStringCache.getValue();
  }

  /**
   * This method builds the value {@link #toString toString()} will return by concatenating the
   * header's string representation and the payload's string representation. If you write this
   * subclass which represents a packet with extra parts (e.g. a trailer), you need to override this
   * method.
   *
   * @return a string representation of this object
   */
  protected String buildString() {
    StringBuilder sb = new StringBuilder();

    if (getHeader() != null) {
      sb.append(getHeader().toString());
    }
    if (getPayload() != null) {
      sb.append(getPayload().toString());
    }

    return sb.toString();
  }

  /**
   * Returns a string representation of the object. This method calls {@link #buildString
   * buildString()} and caches the return value when it is called for the first time, and then, this
   * method returns the cached value from the second time.
   */
  @Override
  public String toString() {
    return stringCache.getValue();
  }

  /**
   * Indicates whether some other object is "equal to" this one. This method firstly compares this
   * packet's header using the header's equals(Object) method, then compares this packet's payload
   * using the payload's equals(Object) method. If you write this subclass with fields which
   * represent somethings other than header or payload, you need to override this method.
   */
  @Override
  public boolean equals(Object obj) {
    if (obj == this) {
      return true;
    }
    if (!this.getClass().isInstance(obj)) {
      return false;
    }

    Packet other = (Packet) obj;

    if (this.getHeader() == null || other.getHeader() == null) {
      if (!(this.getHeader() == null && other.getHeader() == null)) {
        return false;
      }
    } else {
      if (!this.getHeader().equals(other.getHeader())) {
        return false;
      }
    }

    if (this.getPayload() == null || other.getPayload() == null) {
      if (!(this.getPayload() == null && other.getPayload() == null)) {
        return false;
      } else {
        return true;
      }
    } else {
      return this.getPayload().equals(other.getPayload());
    }
  }

  /**
   * This method calculates the value {@link #hashCode hashCode()} will return using the header's
   * hash code and the payload's hash code. If you write this subclass which represents a packet
   * with extra parts (e.g. a trailer), you need to override this method.
   *
   * @return a calculated hash code value for the object
   */
  protected int calcHashCode() {
    int result = 17;
    if (getHeader() != null) {
      result = 31 * result + getHeader().hashCode();
    }
    if (getPayload() != null) {
      result = 31 * result + getPayload().hashCode();
    }
    return result;
  }

  /**
   * Returns a hash code value for the object. This method calls {@link #calcHashCode
   * calcHashCode()} and caches the return value when it is called for the first time, and then,
   * this method returns the cached value from the second time.
   */
  @Override
  public int hashCode() {
    return hashCodeCache.getValue();
  }

  /**
   * Abstract packet builder class.
   *
   * @author Kaito Yamada
   * @version pcap4j 0.9.9
   */
  public abstract static class AbstractBuilder implements Builder {

    @Override
    public Iterator iterator() {
      return new BuilderIterator(this);
    }

    @Override
    public  T get(Class clazz) {
      for (Builder b : this) {
        if (clazz.isInstance(b)) {
          return clazz.cast(b);
        }
      }
      return null;
    }

    @Override
    public Builder getOuterOf(Class clazz) {
      for (Builder b : this) {
        if (clazz.isInstance(b.getPayloadBuilder())) {
          return b;
        }
      }
      return null;
    }

    @Override
    public AbstractBuilder payloadBuilder(Builder payloadBuilder) {
      throw new UnsupportedOperationException();
    }

    @Override
    public Builder getPayloadBuilder() {
      return null;
    }

    @Override
    public abstract Packet build();
  }

  /**
   * Abstract immutable header class.
   *
   * @author Kaito Yamada
   * @version pcap4j 0.9.1
   */
  public abstract static class AbstractHeader implements Header {

    /** */
    private static final long serialVersionUID = -8916517326403680608L;

    private final LazyValue lengthCache;
    private final LazyValue rawDataCache;
    private final LazyValue hexStringCache;
    private final LazyValue stringCache;
    private final LazyValue hashCodeCache;

    /** */
    protected AbstractHeader() {
      this.lengthCache =
          new LazyValue(
              new BuildValueCommand() {
                @Override
                public Integer buildValue() {
                  return calcLength();
                }
              });
      this.rawDataCache =
          new LazyValue(
              new BuildValueCommand() {
                @Override
                public byte[] buildValue() {
                  return buildRawData();
                }
              });
      this.hexStringCache =
          new LazyValue(
              new BuildValueCommand() {
                @Override
                public String buildValue() {
                  return buildHexString();
                }
              });
      this.stringCache =
          new LazyValue(
              new BuildValueCommand() {
                @Override
                public String buildValue() {
                  return buildString();
                }
              });
      this.hashCodeCache =
          new LazyValue(
              new BuildValueCommand() {
                @Override
                public Integer buildValue() {
                  return calcHashCode();
                }
              });
    }

    /**
     * Returns a list of byte arrays which represents this header's fields. This method is called by
     * {@link #calcLength calcLength()} and {@link #buildRawData buildRawData()}.
     *
     * @return a list of byte arrays which represents this header's fields
     */
    protected abstract List getRawFields();

    /**
     * This method calculates the value {@link #length length()} will return by adding up the
     * lengths of byte arrays in the list {@link #getRawFields getRawFields()} returns.
     *
     * @return a calculated length
     */
    protected int calcLength() {
      int length = 0;
      for (byte[] rawField : getRawFields()) {
        length += rawField.length;
      }
      return length;
    }

    /**
     * Returns the header length in bytes. This method calls {@link #calcLength calcLength()} and
     * caches the return value when it is called for the first time, and then, this method returns
     * the cached value from the second time.
     */
    @Override
    public int length() {
      return lengthCache.getValue();
    }

    /**
     * This method builds the value {@link #getRawData getRawData()} will return by concatenating
     * the byte arrays in the list {@link #getRawFields getRawFields()} returns.
     *
     * @return a raw data built
     */
    protected byte[] buildRawData() {
      return ByteArrays.concatenate(getRawFields());
    }

    /**
     * Returns this header's raw data. This method calls {@link #buildRawData buildRawData()} and
     * caches the return value when it is called for the first time, and then, this method returns
     * the cached value from the second time. More correctly, this method returns a copy of the
     * cached value, so that the cache can't be changed.
     */
    @Override
    public byte[] getRawData() {
      byte[] rawData = rawDataCache.getValue();

      byte[] copy = new byte[rawData.length];
      System.arraycopy(rawData, 0, copy, 0, copy.length);
      return copy;
    }

    /**
     * This method builds the value {@link #toHexString toHexString()} will return using the return
     * value of {@link #getRawData getRawData()}. Each octet in this return value is separated by a
     * white space. (e.g. 00 01 02 03 aa bb cc)
     *
     * @return a hex string representation of this object
     */
    protected String buildHexString() {
      return ByteArrays.toHexString(getRawData(), " ");
    }

    /**
     * Returns the hex string representation of this object. This method calls {@link
     * #buildHexString buildHexString()} and caches the return value when it is called for the first
     * time, and then, this method returns the cached value from the second time.
     *
     * @return a hex string representation of this object
     */
    public String toHexString() {
      return hexStringCache.getValue();
    }

    /**
     * This method builds the value {@link #toString toString()} will return.
     *
     * @return a string representation of this object
     */
    protected String buildString() {
      StringBuilder sb = new StringBuilder();
      String ls = System.getProperty("line.separator");

      sb.append("[A header (").append(length()).append(" bytes)]").append(ls);
      sb.append("  Hex stream: ").append(ByteArrays.toHexString(getRawData(), " ")).append(ls);

      return sb.toString();
    }

    /**
     * Returns a string representation of the object. This method calls {@link #buildString
     * buildString()} and caches the return value when it is called for the first time, and then,
     * this method returns the cached value from the second time.
     */
    @Override
    public String toString() {
      return stringCache.getValue();
    }

    /**
     * Indicates whether some other object is "equal to" this one using return values of {@link
     * #getRawData getRawData()}. This method should be overridden so that it does more strict
     * comparisons more efficiently.
     */
    @Override
    public boolean equals(Object obj) {
      if (obj == this) {
        return true;
      }
      if (!this.getClass().isInstance(obj)) {
        return false;
      }
      return Arrays.equals(getClass().cast(obj).getRawData(), getRawData());
    }

    /**
     * This method builds the value {@link #hashCode hashCode()} will return using the byte array
     * {@link #getRawData getRawData()} returns. This method may be better to be overridden for
     * performance reason.
     *
     * @return a calculated hash code value for the object
     */
    protected int calcHashCode() {
      return Arrays.hashCode(getRawData());
    }

    /**
     * Returns a hash code value for the object. This method calls {@link #calcHashCode
     * calcHashCode()} and caches the return value when it is called for the first time, and then,
     * this method returns the cached value from the second time.
     */
    @Override
    public int hashCode() {
      return hashCodeCache.getValue();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy