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

pcap.codec.tcp.Tcp Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2020-2023 Pcap Project
 * SPDX-License-Identifier: MIT OR Apache-2.0
 */
package pcap.codec.tcp;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.Objects;
import pcap.codec.AbstractPacket;
import pcap.common.util.Strings;
import pcap.common.util.Validate;
import pcap.spi.PacketBuffer;

/*
   0                   1                   2                   3
   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |          Source Port          |       Destination Port        |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                        Sequence Number                        |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                    Acknowledgment Number                      |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |  Data |           |U|A|P|R|S|F|                               |
  | Offset| Reserved  |R|C|S|S|Y|I|            Window             |
  |       |           |G|K|H|T|N|N|                               |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |           Checksum            |         Urgent Pointer        |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                    Options                    |    Padding    |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                             data                              |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
/**
 * TCP
 *
 * @since 1.0.0
 */
public final class Tcp extends AbstractPacket {

  public static final int TYPE = 6;

  // Header fields offset.
  private final long sourcePortOffset;
  private final long destinationPortOffset;
  private final long sequenceNumberOffset;
  private final long acknowledgmentNumberOffset;
  private final long dataOffset;
  private final long windowSizeOffset;
  private final long checksumOffset;
  private final long urgentPointerOffset;
  private final long optionsOffset;

  private final long maxDataOffset;

  private Tcp(PacketBuffer buffer) {
    super(buffer);
    this.sourcePortOffset = superOffset;
    this.destinationPortOffset = sourcePortOffset + 2;
    this.sequenceNumberOffset = destinationPortOffset + 2;
    this.acknowledgmentNumberOffset = sequenceNumberOffset + 4;
    this.dataOffset = acknowledgmentNumberOffset + 4;
    this.windowSizeOffset = dataOffset + 2;
    this.checksumOffset = windowSizeOffset + 2;
    this.urgentPointerOffset = checksumOffset + 2;
    this.optionsOffset = urgentPointerOffset + 2;
    this.maxDataOffset = dataOffset();
  }

  /**
   * Wrap buffer into {@link Tcp}.
   *
   * @param size {@link Tcp} header size.
   * @param buffer buffer.
   * @return returns {@link Tcp} instance.
   * @since 1.0.0
   */
  public static Tcp newInstance(int size, PacketBuffer buffer) {
    Validate.notIllegalArgument(
        size >= 20 && size <= 60 && buffer.readableBytes() >= 20, "buffer size is not sufficient.");
    int flags = buffer.getShort(buffer.readerIndex() + 12) & 0x1FF;
    buffer.setShort(buffer.readerIndex() + 12, flags & 0x1FF | (size >> 2) << 12);
    return new Tcp(buffer);
  }

  /**
   * Get source port.
   *
   * @return returns source port.
   * @since 1.0.0
   */
  public int sourcePort() {
    return superBuffer.getShort(sourcePortOffset) & 0xFFFF;
  }

  /**
   * Set source port.
   *
   * @param value source port.
   * @return returns this instance.
   * @since 1.0.0
   */
  public Tcp sourcePort(int value) {
    superBuffer.setShort(sourcePortOffset, value & 0xFFFF);
    return this;
  }

  /**
   * Get destination port.
   *
   * @return returns destination port.
   * @since 1.0.0
   */
  public int destinationPort() {
    return superBuffer.getShort(destinationPortOffset) & 0xFFFF;
  }

  /**
   * Set destination port.
   *
   * @param value destination port.
   * @return returns this instance.
   * @since 1.0.0
   */
  public Tcp destinationPort(int value) {
    superBuffer.setShort(destinationPortOffset, value & 0xFFFF);
    return this;
  }

  /**
   * Get sequence number.
   *
   * @return returns sequence number.
   * @since 1.0.0
   */
  public long sequenceNumber() {
    return superBuffer.getUnsignedInt(sequenceNumberOffset);
  }

  /**
   * Set sequence number.
   *
   * @param value sequence number.
   * @return returns this instance.
   * @since 1.0.0
   */
  public Tcp sequenceNumber(int value) {
    superBuffer.setInt(sequenceNumberOffset, value);
    return this;
  }

  /**
   * Get acknowledgment number.
   *
   * @return returns acknowledgment number.
   * @since 1.0.0
   */
  public long acknowledgmentNumber() {
    return superBuffer.getUnsignedInt(acknowledgmentNumberOffset);
  }

  /**
   * Set acknowledgment number.
   *
   * @param value acknowledgment number.
   * @return returns this instance.
   * @since 1.0.0
   */
  public Tcp acknowledgmentNumber(int value) {
    superBuffer.setInt(acknowledgmentNumberOffset, value);
    return this;
  }

  /**
   * Get data offset.
   *
   * @return returns data offset.
   * @since 1.0.0
   */
  public int dataOffset() {
    return (superBuffer.getShort(dataOffset) >> 12) & 0xF;
  }

  /**
   * Set data offset.
   *
   * @param value data offset.
   * @return returns this instance.
   * @since 1.0.0
   */
  public Tcp dataOffset(int value) {
    if (value < 5 || value > maxDataOffset) {
      throw new IllegalArgumentException(
          String.format("value: %d (expected: 5 >= value <= %d)", value, maxDataOffset));
    }
    int val = getShortFlags() & 0x1FF | (value & 0xF) << 12;
    superBuffer.setShort(dataOffset, val);
    return this;
  }

  /**
   * Get NS flag.
   *
   * @return returns {@code true} if is set, {@code false} otherwise.
   * @since 1.3.0
   */
  public boolean isNs() {
    return (((superBuffer.getShort(dataOffset) & 0x1FF) >> 8) & 0x1) == 1;
  }

  /**
   * Set NS flag.
   *
   * @param value {@code true} for set NS flag, {@code false} otherwise.
   * @return returns this instance.
   * @since 1.0.0
   */
  public Tcp ns(boolean value) {
    if (isNs() != value) {
      setShortFlags(value ? 256 : -256);
    }
    return this;
  }

  /**
   * Get CWR flag.
   *
   * @return returns {@code true} if is set, {@code false} otherwise.
   * @since 1.3.0
   */
  public boolean isCwr() {
    return (((superBuffer.getShort(dataOffset) & 0x1FF) >> 7) & 0x1) == 1;
  }

  /**
   * Set CWR flag.
   *
   * @param value {@code true} for set CWR flag, {@code false} otherwise.
   * @return returns this instance.
   * @since 1.0.0
   */
  public Tcp cwr(boolean value) {
    if (isCwr() != value) {
      setShortFlags(value ? 128 : -128);
    }
    return this;
  }

  /**
   * Get ECE flag.
   *
   * @return returns {@code true} if is set, {@code false} otherwise.
   * @since 1.3.0
   */
  public boolean isEce() {
    return (((superBuffer.getShort(dataOffset) & 0x1FF) >> 6) & 0x1) == 1;
  }

  /**
   * Set ECE flag.
   *
   * @param value {@code true} for set ECE flag, {@code false} otherwise.
   * @return returns this instance.
   * @since 1.0.0
   */
  public Tcp ece(boolean value) {
    if (isEce() != value) {
      setShortFlags(value ? 64 : -64);
    }
    return this;
  }

  /**
   * Get URG flag.
   *
   * @return returns {@code true} if is set, {@code false} otherwise.
   * @since 1.3.0
   */
  public boolean isUrg() {
    return (((superBuffer.getShort(dataOffset) & 0x1FF) >> 5) & 0x1) == 1;
  }

  /**
   * Set URG flag.
   *
   * @param value {@code true} for set URG flag, {@code false} otherwise.
   * @return returns this instance.
   * @since 1.0.0
   */
  public Tcp urg(boolean value) {
    if (isUrg() != value) {
      setShortFlags(value ? 32 : -32);
    }
    return this;
  }

  /**
   * Get ACK flag.
   *
   * @return returns {@code true} if is set, {@code false} otherwise.
   * @since 1.3.0
   */
  public boolean isAck() {
    return (((superBuffer.getShort(dataOffset) & 0x1FF) >> 4) & 0x1) == 1;
  }

  /**
   * Set ACK flag.
   *
   * @param value {@code true} for set ACK flag, {@code false} otherwise.
   * @return returns this instance.
   * @since 1.0.0
   */
  public Tcp ack(boolean value) {
    if (isAck() != value) {
      setShortFlags(value ? 16 : -16);
    }
    return this;
  }

  /**
   * Get PSH flag.
   *
   * @return returns {@code true} if is set, {@code false} otherwise.
   * @since 1.3.0
   */
  public boolean isPsh() {
    return (((superBuffer.getShort(dataOffset) & 0x1FF) >> 3) & 0x1) == 1;
  }

  /**
   * Set PSH flag.
   *
   * @param value {@code true} for set PSH flag, {@code false} otherwise.
   * @return returns this instance.
   * @since 1.0.0
   */
  public Tcp psh(boolean value) {
    if (isPsh() != value) {
      setShortFlags(value ? 8 : -8);
    }
    return this;
  }

  /**
   * Get RST flag.
   *
   * @return returns {@code true} if is set, {@code false} otherwise.
   * @since 1.3.0
   */
  public boolean isRst() {
    return (((superBuffer.getShort(dataOffset) & 0x1FF) >> 2) & 0x1) == 1;
  }

  /**
   * Set RST flag.
   *
   * @param value {@code true} for set RST flag, {@code false} otherwise.
   * @return returns this instance.
   * @since 1.0.0
   */
  public Tcp rst(boolean value) {
    if (isRst() != value) {
      setShortFlags(value ? 4 : -4);
    }
    return this;
  }

  /**
   * Get SYN flag.
   *
   * @return returns {@code true} if is set, {@code false} otherwise.
   * @since 1.3.0
   */
  public boolean isSyn() {
    return (((superBuffer.getShort(dataOffset) & 0x1FF) >> 1) & 0x1) == 1;
  }

  /**
   * Set SYN flag.
   *
   * @param value {@code true} for set SYN flag, {@code false} otherwise.
   * @return returns this instance.
   * @since 1.0.0
   */
  public Tcp syn(boolean value) {
    if (isSyn() != value) {
      setShortFlags(value ? 2 : -2);
    }
    return this;
  }

  /**
   * Get FIN flag.
   *
   * @return returns {@code true} if is set, {@code false} otherwise.
   * @since 1.3.0
   */
  public boolean isFin() {
    return ((superBuffer.getShort(dataOffset) & 0x1FF) & 0x1) == 1;
  }

  /**
   * Set FIN flag.
   *
   * @param value {@code true} for set FIN flag, {@code false} otherwise.
   * @return returns this instance.
   * @since 1.0.0
   */
  public Tcp fin(boolean value) {
    if (isFin() != value) {
      setShortFlags(value ? 1 : -1);
    }
    return this;
  }

  /**
   * Get window size.
   *
   * @return returns window size.
   * @since 1.0.0
   */
  public int windowSize() {
    return superBuffer.getShort(windowSizeOffset) & 0xFFFF;
  }

  /**
   * Set window size.
   *
   * @param value window size.
   * @return returns this instance.
   * @since 1.0.0
   */
  public Tcp windowSize(int value) {
    superBuffer.setShort(windowSizeOffset, value & 0xFFFF);
    return this;
  }

  /**
   * Get checksum.
   *
   * @return returns checksum.
   * @since 1.0.0
   */
  public int checksum() {
    return superBuffer.getShort(checksumOffset) & 0xFFFF;
  }

  /**
   * Set checksum, calculate checksum with {@link Tcp#calculateChecksum(InetAddress, InetAddress,
   * int)}.
   *
   * @param value checksum
   * @return returns this instance.
   * @since 1.0.0
   */
  public Tcp checksum(int value) {
    superBuffer.setShort(checksumOffset, value & 0xFFFF);
    return this;
  }

  /**
   * Calculate this header checksum.
   *
   * @param srcAddr source IP address.
   * @param dstAddr destination IP address.
   * @param payloadLength payload length.
   * @return returns checksum.
   * @since 1.0.0
   */
  public int calculateChecksum(InetAddress srcAddr, InetAddress dstAddr, int payloadLength) {
    return Checksum.calculate(
        superBuffer, superOffset, srcAddr, dstAddr, TYPE, size(), payloadLength);
  }

  /**
   * Check if this header has valid checksum.
   *
   * @param src source IP address.
   * @param dst destination IP address.
   * @param payloadLength payload length.
   * @return returns {@code true} if valid, {@code false} otherwise.
   * @since 1.0.0
   */
  public boolean isValidChecksum(Inet4Address src, Inet4Address dst, int payloadLength) {
    return calculateChecksum(src, dst, payloadLength) == 0;
  }

  /**
   * Get urgent pointer.
   *
   * @return returns urgent pointer.
   * @since 1.0.0
   */
  public int urgentPointer() {
    return superBuffer.getShort(urgentPointerOffset) & 0xFFFF;
  }

  /**
   * Set urgent pointer.
   *
   * @param value urgent pointer.
   * @return returns this instance.
   * @since 1.0.0
   */
  public Tcp urgentPointer(int value) {
    superBuffer.setShort(urgentPointerOffset, value & 0xFFFF);
    return this;
  }

  /**
   * Get header options.
   *
   * @return returns header options.
   * @since 1.0.0
   */
  public byte[] options() {
    byte[] data = new byte[(dataOffset() << 2) - 20];
    superBuffer.getBytes(optionsOffset, data, 0, data.length);
    return data;
  }

  /**
   * Set header options.
   *
   * @param value options value.
   * @return returns this instance.
   * @since 1.0.0
   */
  public Tcp options(byte[] value) {
    int maxLength = (dataOffset() - 5) << 2;
    superBuffer.setBytes(optionsOffset, value, 0, Math.min(value.length, maxLength));
    return this;
  }

  private short getShortFlags() {
    int val = superBuffer.getShort(dataOffset) & 0x1FF;
    short flags = 0;
    for (int i = 8; i >= 0; i--) {
      if (((val >> i) & 0x1) == 1) {
        flags += 1 << i;
      }
    }
    return flags;
  }

  private void setShortFlags(int flags) {
    int val = (getShortFlags() + flags) & 0x1FF | dataOffset() << 12;
    superBuffer.setShort(dataOffset, val);
  }

  /** {@inheritDoc} */
  @Override
  public int size() {
    if (maxDataOffset == 0) {
      Validate.notIllegalState(superBuffer.readableBytes() >= 20, "buffer size is not sufficient.");
      return ((superBuffer.getShort(superBuffer.readerIndex() + 12) >> 12) & 0xF) << 2;
    }
    return dataOffset() << 2;
  }

  @Override
  public boolean equals(Object o) {
    return super.equals(o);
  }

  @Override
  public int hashCode() {
    return Objects.hash(
        super.hashCode(),
        sourcePort(),
        destinationPort(),
        sequenceNumber(),
        acknowledgmentNumber(),
        dataOffset(),
        isNs(),
        isCwr(),
        isEce(),
        isUrg(),
        isAck(),
        isPsh(),
        isRst(),
        isSyn(),
        isFin(),
        windowSize(),
        checksum(),
        urgentPointer(),
        Arrays.hashCode(options()));
  }

  @Override
  public String toString() {
    short v = superBuffer.getShort(dataOffset);
    return Strings.toStringBuilder(this)
        .add("sourcePort", sourcePort())
        .add("destinationPort", destinationPort())
        .add("sequenceNumber", sequenceNumber())
        .add("acknowledgmentNumber", acknowledgmentNumber())
        .add("dataOffset", (v >> 12) & 0xF)
        .add("ns", (((v & 0x1FF) >> 8) & 0x1) == 1)
        .add("cwr", (((v & 0x1FF) >> 7) & 0x1) == 1)
        .add("ece", (((v & 0x1FF) >> 6) & 0x1) == 1)
        .add("urg", (((v & 0x1FF) >> 5) & 0x1) == 1)
        .add("ack", (((v & 0x1FF) >> 4) & 0x1) == 1)
        .add("psh", (((v & 0x1FF) >> 3) & 0x1) == 1)
        .add("rst", (((v & 0x1FF) >> 2) & 0x1) == 1)
        .add("syn", (((v & 0x1FF) >> 1) & 0x1) == 1)
        .add("fin", ((v & 0x1FF) & 0x1) == 1)
        .add("windowsSize", windowSize())
        .add("checksum", String.format("0x%s", Integer.toHexString(checksum())))
        .add("urgentPointer", urgentPointer())
        .add("options", String.format("0x%s", Strings.hex(options())))
        .toString();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy