/* * $Id: TCPPacket.java 6023 2005-12-10 20:42:15Z dfs $ * * Copyright 2004-2005 Daniel F. Savarese * Contact Information: http://www.savarese.org/contact.html * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.savarese.org/software/ApacheLicense-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.savarese.vserv.tcpip; /** * TCPPacket extends {@link IPPacket} to handle TCP packets. The TCP * packet structure is described in * <a href="http://www.ietf.org/rfc/rfc0761.txt?number=761">RFC 761</a>. * * @author <a href="http://www.savarese.org/">Daniel F. Savarese</a> */ public class TCPPacket extends IPPacket { /** Offset into the TCP packet of the source port header value. */ public static final int OFFSET_SOURCE_PORT = 0; /** Offset into the TCP packet of the destination port header value. */ public static final int OFFSET_DESTINATION_PORT = 2; /** Offset into the TCP packet of the sequence number header value. */ public static final int OFFSET_SEQUENCE = 4; /** Offset into the TCP packet of the acknowledgement number header value. */ public static final int OFFSET_ACK = 8; /** Offset into the TCP packet of the TCP header length. */ public static final int OFFSET_HEADER_LENGTH = 12; /** Offset into the TCP packet of the control header. */ public static final int OFFSET_CONTROL = 13; /** Offset into the TCP packet of the window size. */ public static final int OFFSET_WINDOW_SIZE = 14; /** Offset into the TCP packet of the TCP checksum. */ public static final int OFFSET_TCP_CHECKSUM = 16; /** Offset into the TCP packet of the URG pointer. */ public static final int OFFSET_URG_POINTER = 18; /** A mask for extracting the FIN bit from the control header. */ public static final int MASK_FIN = 0x01; /** A mask for extracting the SYN bit from the control header. */ public static final int MASK_SYN = 0x02; /** A mask for extracting the reset bit from the control header. */ public static final int MASK_RST = 0x04; /** A mask for extracting the push bit from the control header. */ public static final int MASK_PSH = 0x08; /** A mask for extracting the ACK bit from the control header. */ public static final int MASK_ACK = 0x10; /** A mask for extracting the urgent bit from the control header. */ public static final int MASK_URG = 0x20; /** A byte value for TCP options indicating end of option list. */ public static final byte KIND_EOL = 0; /** A byte value for TCP options indicating no operation. */ public static final byte KIND_NOP = 1; /** * A byte value for TCP options identifying a selective * acknowledgement option. */ public static final byte KIND_SACK = 4; /** The byte offset into the IP packet where the TCP packet begins. */ private int __offset; /** * Creates a new TCP packet of a given size. * * @param size The number of bytes in the packet. */ public TCPPacket(int size) { super(size); __offset = 0; } /** * Creates a new TCP packet that is a copy of a given packet. * * @param packet The packet to replicate. */ public TCPPacket(TCPPacket packet) { super(packet.size()); copy(packet); __offset = packet.__offset; } /** * Clears all selective acknowledgement options. This is a * temporary kluge and will be removed from the final API. Do not * use it. The final API will have proper methods for adjusting * selective acknowledgement options. */ public void clearSACK() { int headerLength = getTCPHeaderByteLength(); int offset = OFFSET_URG_POINTER + 2; if(headerLength > offset) { offset+=__offset; headerLength+=__offset; loop: do { byte kind = _data_[offset]; switch(kind) { case KIND_NOP: ++offset; break; case KIND_EOL: break loop; case KIND_SACK: _data_[offset] = KIND_NOP; _data_[offset + 1] = KIND_NOP; break loop; //break; default: offset+=_data_[offset + 1]; /* int length = _data_[offset + 1]; while(length-- > 0) _data_[offset++] = KIND_NOP; */ break; } } while(offset < headerLength); } } /** * Copies the contents of a TCPPacket. If the current data array is * of insufficient length to store the contents, a new array is * allocated. * * @param packet The TCPPacket to copy. */ public final void copyData(TCPPacket packet) { if(_data_.length < packet._data_.length) { byte[] data = new byte[packet._data_.length]; System.arraycopy(_data_, 0, data, 0, getCombinedHeaderByteLength()); _data_ = data; } int length = packet.getTCPDataByteLength(); System.arraycopy(packet._data_, packet.getCombinedHeaderByteLength(), _data_, getCombinedHeaderByteLength(), length); setTCPDataByteLength(length); } /** * @param mask The bit mask to check. * @return True only if all of the bits in the mask are set. */ public boolean isSet(int mask) { return ((_data_[__offset + OFFSET_CONTROL] & mask) == mask); } /** * @param mask The bit mask to check. * @return True if any of the bits in the mask are set. */ public boolean isSetAny(int mask) { return ((_data_[__offset + OFFSET_CONTROL] & mask) != 0); } /** * @param mask The bit mask to check. * @return True only if all of the bits in the mask are set * and ONLY the bits in the mask are set. */ public boolean isSetOnly(int mask) { int flags = _data_[__offset + OFFSET_CONTROL] & 0xff; return ((flags & mask) == flags); } /** * Sets the specified control bits without altering any other bits * in the control header. * * @param mask The bits to set. */ public void addControlFlags(int mask) { int flags = _data_[__offset + OFFSET_CONTROL] & 0xff; flags |= mask; _data_[__offset + OFFSET_CONTROL] = (byte)(flags & 0xff); } /** * Unsets the specified control bits. * * @param mask The bits to unset. */ public void removeControlFlags(int mask) { int flags = _data_[__offset + OFFSET_CONTROL] & 0xff; flags |= mask; flags ^= mask; _data_[__offset + OFFSET_CONTROL] = (byte)(flags & 0xff); } /** * Sets the control header to the sepecified value. * * @param mask The new control header bit mask. */ public void setControlFlags(int mask) { _data_[__offset + OFFSET_CONTROL] = (byte)(mask & 0xff); } public void setData(byte[] data) { super.setData(data); __offset = getIPHeaderByteLength(); } /** * Sets the source port. * * @param port The new source port. */ public final void setSourcePort(int port) { _data_[__offset + OFFSET_SOURCE_PORT] = (byte)((port >> 8) & 0xff); _data_[__offset + OFFSET_SOURCE_PORT + 1] = (byte)(port & 0xff); } /** * Sets the destination port. * * @param port The new destination port. */ public final void setDestinationPort(int port) { _data_[__offset + OFFSET_DESTINATION_PORT] = (byte)((port >> 8) & 0xff); _data_[__offset + OFFSET_DESTINATION_PORT + 1] = (byte)(port & 0xff); } /** * @return The source port. */ public final int getSourcePort() { return (((_data_[__offset + OFFSET_SOURCE_PORT] & 0xff) << 8) | (_data_[__offset + OFFSET_SOURCE_PORT + 1] & 0xff)); } /** * @return The destination port. */ public final int getDestinationPort() { return (((_data_[__offset + OFFSET_DESTINATION_PORT] & 0xff) << 8) | (_data_[__offset + OFFSET_DESTINATION_PORT + 1] & 0xff)); } /** * Sets the sequence number. * * @param seq The new sequence number. */ public final void setSequenceNumber(long seq) { OctetConverter.intToOctets((int)(seq & 0xffffffff), _data_, __offset + OFFSET_SEQUENCE); } /** * @return The sequence number. */ public final long getSequenceNumber() { return (((_data_[__offset + OFFSET_SEQUENCE] & 0xffL) << 24) | ((_data_[__offset + OFFSET_SEQUENCE + 1] & 0xffL) << 16) | ((_data_[__offset + OFFSET_SEQUENCE + 2] & 0xffL) << 8) | (_data_[__offset + OFFSET_SEQUENCE + 3] & 0xffL)); } /** * Sets the acknowledgement number. * * @param seq The new acknowledgement number. */ public final void setAckNumber(long seq) { OctetConverter.intToOctets((int)(seq & 0xffffffff), _data_, __offset + OFFSET_ACK); } /** * @return The acknowledgement number. */ public final long getAckNumber() { return (((_data_[__offset + OFFSET_ACK] & 0xffL) << 24) | ((_data_[__offset + OFFSET_ACK + 1] & 0xffL) << 16) | ((_data_[__offset + OFFSET_ACK + 2] & 0xffL) << 8) | (_data_[__offset + OFFSET_ACK + 3] & 0xffL)); } public void setIPHeaderLength(int length) { super.setIPHeaderLength(length); __offset = getIPHeaderByteLength(); } /** * Sets te TCP header length (i.e., the data offset field) in 32-bit words. * * @param length The TCP header length in 32-bit words. */ public final void setTCPHeaderLength(int length) { _data_[__offset + OFFSET_HEADER_LENGTH] &= 0x0f; _data_[__offset + OFFSET_HEADER_LENGTH] |= ((length << 4) & 0xf0); } /** * @return The TCP header length in 32-bit words. */ public final int getTCPHeaderLength() { return (_data_[__offset + OFFSET_HEADER_LENGTH] & 0xf0) >> 4; } /** * @return The TCP header length in bytes. */ public final int getTCPHeaderByteLength() { return getTCPHeaderLength() << 2; } /** * Sets the TCP window size. * * @param window The TCP window size. */ public final void setWindowSize(int window) { _data_[__offset + OFFSET_WINDOW_SIZE] = (byte)((window >> 8) & 0xff); _data_[__offset + OFFSET_WINDOW_SIZE + 1] = (byte)(window & 0xff); } /** * @return The TCP window size. */ public final int getWindowSize() { return (((_data_[__offset + OFFSET_WINDOW_SIZE] & 0xff) << 8) | (_data_[__offset + OFFSET_WINDOW_SIZE + 1] & 0xff)); } /** * Sets the urgent pointer. * * @param pointer The urgent pointer value. */ public final void setUrgentPointer(int pointer) { _data_[__offset + OFFSET_URG_POINTER] = (byte)((pointer >> 8) & 0xff); _data_[__offset + OFFSET_URG_POINTER + 1] = (byte)(pointer & 0xff); } /** * @return The urgent pointer value. */ public final int getUrgentPointer() { return (((_data_[__offset + OFFSET_URG_POINTER] & 0xff) << 8) | (_data_[__offset + OFFSET_URG_POINTER + 1] & 0xff)); } /** * @return The TCP checksum. */ public final int getTCPChecksum() { return (((_data_[__offset + OFFSET_TCP_CHECKSUM] & 0xff) << 8) | (_data_[__offset + OFFSET_TCP_CHECKSUM + 1] & 0xff)); } /** * @return The TCP packet length in bytes. This is the size of the * IP packet minus the size of the IP header. */ public final int getTCPPacketByteLength() { return getIPPacketLength() - __offset; } /** * @return The IP header length plus the TCP header length in bytes. */ public final int getCombinedHeaderByteLength() { return __offset + getTCPHeaderByteLength(); } /** * Sets the length of the TCP data payload. * * @param length The length of the TCP data payload in bytes. */ public final void setTCPDataByteLength(int length) { if(length < 0) length = 0; setIPPacketLength(getCombinedHeaderByteLength() + length); } public final int getTCPDataByteLength() { return getIPPacketLength() - getCombinedHeaderByteLength(); } private final int __getVirtualHeaderTotal() { int s1 = ((_data_[OFFSET_SOURCE_ADDRESS] & 0xff) << 8) | (_data_[OFFSET_SOURCE_ADDRESS + 1] & 0xff); int s2 = ((_data_[OFFSET_SOURCE_ADDRESS + 2] & 0xff) << 8) | (_data_[OFFSET_SOURCE_ADDRESS + 3] & 0xff); int d1 = ((_data_[OFFSET_DESTINATION_ADDRESS] & 0xff) << 8) | (_data_[OFFSET_DESTINATION_ADDRESS + 1] & 0xff); int d2 = ((_data_[OFFSET_DESTINATION_ADDRESS + 2] & 0xff) << 8) | (_data_[OFFSET_DESTINATION_ADDRESS + 3] & 0xff); return s1 + s2 + d1 + d2 + getProtocol() + getTCPPacketByteLength(); } /** * Computes the TCP checksum, optionally updating the TCP checksum header. * * @param update Specifies whether or not to update the TCP checksum * header after computing the checksum. A value of true indicates * the header should be updated, a value of false indicates it * should not be updated. * @return The computed TCP checksum. */ public final int computeTCPChecksum(boolean update) { return _computeChecksum_(__offset, __offset + OFFSET_TCP_CHECKSUM, getIPPacketLength(), __getVirtualHeaderTotal(), update); } /** * Same as <code>computeTCPChecksum(true);</code> * * @return The computed TCP checksum value. */ public final int computeTCPChecksum() { return computeTCPChecksum(true); } }