/* * Copyright (c) 2006-2007 Graz University of Technology. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The names "Graz University of Technology" and "IAIK of Graz University of * Technology" must not be used to endorse or promote products derived from * this software without prior written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package ejip.jtcpip; /** * Encapsulating methods to handle a Payload as a TCP Packet. All methods are * static and get a {@link Payload} as a parameter. There are get and set * methods for all fields of the TCP header. * * @author Tobias Kellner * @author Ulrich Feichter * @author Christof Rath * @version $Rev: 939 $ $Date: 2007/09/04 00:56:05 $ */ public class TCPPacket { /** Position of the FIN Flag in the 4th word of the IP header */ public static final int FIN_MASK = 0x10000; /** Position of the SYN Flag in the 4th word of the IP header */ public static final int SYN_MASK = 0x20000; /** Position of the RST Flag in the 4th word of the IP header */ public static final int RST_MASK = 0x40000; /** Position of the PSH Flag in the 4th word of the IP header */ public static final int PSH_MASK = 0x80000; /** Position of the ACK Flag in the 4th word of the IP header */ public static final int ACK_MASK = 0x100000; /** Position of the URG Flag in the 4th word of the IP header */ public static final int URG_MASK = 0x200000; /** * Get the sending port. The value is read from the TCP header. * * @param pay * The Payload * @return The source port */ public static short getSourcePort(Payload pay) { return (short) ((pay.payload[0] >>> 16) & 0xFFFF); } /** * Set the sending port. The value is set in the TCP header. * * @param pay * The Payload * @param port * The source port */ public static void setSourcePort(Payload pay, short port) { int i = pay.payload[0] & 0x0000FFFF; pay.payload[0] = i | (port << 16); } /** * Get the receiving port. The value is read from the TCP header. * * @param pay * The Payload * @return The destination port */ public static short getDestPort(Payload pay) { return (short) (pay.payload[0] & 0xFFFF); } /** * Set the receiving port. The value is set in the TCP header. * * @param pay * The Payload * @param port * The destination port */ public static void setDestPort(Payload pay, short port) { int i = pay.payload[0] & 0xFFFF0000; pay.payload[0] = i | (port & 0xFFFF); } /** * Get the Sequence number. If the SYN flag is present then this is the * initial sequence number and the first data byte is the sequence number * plus 1. Otherwise if the SYN flag is not present then the first data byte * is the sequence number. The value is read from the TCP header. * * @param pay * The Payload * @return The Sequence number */ public static int getSeqNr(Payload pay) { return pay.payload[1]; } /** * Set the Sequence number. If the SYN flag is present then this is the * initial sequence number and the first data byte is the sequence number * plus 1. Otherwise if the SYN flag is not present then the first data byte * is the sequence number. The value is set in the TCP header. * * @param pay * The Payload * @param seqNr * The Sequence number */ public static void setSeqNr(Payload pay, int seqNr) { pay.payload[1] = seqNr; } /** * Get the Acknowledgement number. If the ACK flag is set then the value of * this field is the sequence number the sender expects next. The value is * read from the TCP header. * * @param pay * The Payload * @return The Acknowledgement number */ public static int getAckNr(Payload pay) { return pay.payload[2]; } /** * Set the Acknowledgement number. If the ACK flag is set then the value of * this field is the sequence number the sender expects next. The value is * set in the TCP header. * * @param pay * The Payload * @param ackNr * The Acknowledgement number */ public static void setAckNr(Payload pay, int ackNr) { pay.payload[2] = ackNr; } /** * Get the Data offset. This is the size of the TCP header in 32-bit words. * (min. 5, max. 15) The value is read from the TCP header. * * @param pay * The Payload * @return The Data offset */ public static byte getDataOffset(Payload pay) { return (byte) ((pay.payload[3] >>> 28) & 0x0F); } /** * Set the Data offset. This is the size of the TCP header in 32-bit words. * (min. 5, max. 15) The value is set in the TCP header. * * @param pay * The Payload * @param ofs * The Data offset */ public static void setDataOffset(Payload pay, byte ofs) { int i = pay.payload[3] & 0x0FFFFFFF; pay.payload[3] = i | ((ofs & 0xF) << 28); } // TODO: Reserved field /** * Check whether the FIN Flag is set. No more data from sender. The value is * read from the TCP header. * * @param pay * The Payload * @return Whether the FIN Flag is set */ public static boolean isFINFlagSet(Payload pay) { return (pay.payload[3] & FIN_MASK) != 0; } /** * Set the FIN Flag. No more data from sender. The value is set in the TCP * header. * * @param pay * The Payload */ public static void setFINFlag(Payload pay) { pay.payload[3] = pay.payload[3] | FIN_MASK; } /** * Clear the FIN Flag. No more data from sender. The value is set in the TCP * header. * * @param pay * The Payload */ public static void clearFINFlag(Payload pay) { pay.payload[3] = pay.payload[3] & ~FIN_MASK; } /** * Check whether the SYN Flag is set. Synchronize sequence numbers. The * value is read from the TCP header. * * @param pay * The Payload * @return Whether the SYN Flag is set */ public static boolean isSYNFlagSet(Payload pay) { return (pay.payload[3] & SYN_MASK) != 0; } /** * Set the SYN Flag. Synchronize sequence numbers. The value is set in the * TCP header. * * @param pay * The Payload */ public static void setSYNFlag(Payload pay) { pay.payload[3] = pay.payload[3] | SYN_MASK; } /** * Clear the SYN Flag. Synchronize sequence numbers. The value is set in the * TCP header. * * @param pay * The Payload */ public static void clearSYNFlag(Payload pay) { pay.payload[3] = pay.payload[3] & ~SYN_MASK; } /** * Check whether the RST Flag is set. Reset the connection. The value is * read from the TCP header. * * @param pay * The Payload * @return Whether the RST Flag is set */ public static boolean isRSTFlagSet(Payload pay) { return (pay.payload[3] & RST_MASK) != 0; } /** * Set the RST Flag. Reset the connection. The value is set in the TCP * header. * * @param pay * The Payload */ public static void setRSTFlag(Payload pay) { pay.payload[3] = pay.payload[3] | RST_MASK; } /** * Clear the RST Flag. Reset the connection. The value is set in the TCP * header. * * @param pay * The Payload */ public static void clearRSTFlag(Payload pay) { pay.payload[3] = pay.payload[3] & ~RST_MASK; } /** * Check whether the PSH Flag is set. Push function. The value is read from * the TCP header. * * @param pay * The Payload * @return Whether the PSH Flag is set */ public static boolean isPSHFlagSet(Payload pay) { return (pay.payload[3] & PSH_MASK) != 0; } /** * Set the PSH Flag. Push function. The value is set in the TCP header. * * @param pay * The Payload */ public static void setPSHFlag(Payload pay) { pay.payload[3] = pay.payload[3] | PSH_MASK; } /** * Clear the PSH Flag. Push function. The value is set in the TCP header. * * @param pay * The Payload */ public static void clearPSHFlag(Payload pay) { pay.payload[3] = pay.payload[3] & ~PSH_MASK; } /** * Check whether the ACK Flag is set. Acknowledgement field is significant. * The value is read from the TCP header. * * @param pay * The Payload * @return Whether the ACK Flag is set */ public static boolean isACKFlagSet(Payload pay) { return (pay.payload[3] & ACK_MASK) != 0; } /** * Set the ACK Flag. Acknowledgement field is significant. The value is set * in the TCP header. * * @param pay * The Payload */ public static void setACKFlag(Payload pay) { pay.payload[3] = pay.payload[3] | ACK_MASK; } /** * Clear the ACK Flag. Acknowledgement field is significant. The value is * set in the TCP header. * * @param pay * The Payload */ public static void clearACKFlag(Payload pay) { pay.payload[3] = pay.payload[3] & ~ACK_MASK; } /** * Check whether the URG Flag is set. Urgent pointer field is significant. * The value is read from the TCP header. * * @param pay * The Payload * @return Whether the Flag is set */ public static boolean isURGFlagSet(Payload pay) { return (pay.payload[3] & URG_MASK) != 0; } /** * Set the URG Flag. Urgent pointer field is significant. The value is set * in the TCP header. * * @param pay * The Payload */ public static void setURGFlag(Payload pay) { pay.payload[3] = pay.payload[3] | URG_MASK; } /** * Clear the URG Flag. Urgent pointer field is significant. The value is set * in the TCP header. * * @param pay * The Payload */ public static void clearURGFlag(Payload pay) { pay.payload[3] = pay.payload[3] & ~URG_MASK; } /** * Get the Window size. The number of bytes the sender is willing to receive * starting from the acknowledgement field value. The value is read from the * TCP header. * * @param pay * The Payload * @return The Window */ public static short getWindow(Payload pay) { return (short) (pay.payload[3] & 0xFFFF); } /** * Set the Window size. The number of bytes the sender is willing to receive * starting from the acknowledgement field value. The value is set in the * TCP header. * * @param pay * The Payload * @param wnd * The Window */ public static void setWindow(Payload pay, short wnd) { int i = pay.payload[3] & 0xFFFF0000; pay.payload[3] = i | (wnd & 0xFFFF); } /** * Get the Header Checksum. The checksum is calculated over the TCP header * (+IP Pseudoheader) and the data. The value is read from the TCP header. * * @param pay * The Payload * @return The Header Checksum */ public static short getChecksum(Payload pay) { return (short) ((pay.payload[4] >>> 16) & 0xFFFF); } /** * Set the Header Checksum to the correct value. The checksum is calculated * over the TCP header (+IP Pseudoheader) and the data. (with the checksum * field set to zero). The calculation is done in * {@link TCPPacket#calculateChecksum(Payload)}. The value is set in the * TCP header. * * @param pay * The Payload */ public static void setChecksum(Payload pay) { pay.payload[4] = pay.payload[4] & 0x0000FFFF; pay.payload[4] = pay.payload[4] | (calculateChecksum(pay) << 16); } /** * Get the Urgent pointer. An offset from the sequence number indicating the * last urgent data byte. The value is read from the TCP header. * * @param pay * The Payload * @return The Urgent pointer */ public static short getURGPointer(Payload pay) { return (byte) (pay.payload[4] & 0xFFFF); } /** * Set the Urgent pointer. An offset from the sequence number indicating the * last urgent data byte. The value is set in the TCP header. * * @param pay * The Payload * @param urgP * The Urgent pointer */ public static void setURGPointer(Payload pay, short urgP) { int i = pay.payload[4] & 0xFFFF0000; pay.payload[4] = i | urgP & 0xFFFF; } /** * Calculate the correct Checksum. The checksum is calculated over the TCP * header (+IP Pseudoheader) and the data (with the checksum field assumed * to be zero). * * @param pay * The Payload * @return The Checksum */ public static short calculateChecksum(Payload pay) { return calculateChecksum(pay, pay.length); } /** * Calculate the correct Checksum. The checksum is calculated over the TCP * header (+IP Pseudoheader) and the data (with the checksum field assumed * to be zero). * * @param pay * The Payload * @param tcpPacketLength * The lenght of the packet in case of fragmentation * @return The Checksum */ public static short calculateChecksum(Payload pay, int tcpPacketLength) { // compute over TCP Header and Payload, except the last bytes which // don't // fall on an int boundary int cnt = tcpPacketLength / 4; int i; int ofs = 0; int sum = 0; while (cnt != 0) { i = pay.payload[ofs]; // if (Debug.enabled) // Debug.println("Checksum: " + Debug.intToHexString(i) + " (ofs: " // + ofs + ")", Debug.DBG_TCP); sum += i & 0xFFFF; sum += i >>> 16; ++ofs; --cnt; } int modulo = tcpPacketLength % 4; if (modulo != 0) { i = pay.payload[ofs]; // compute over the last int of the payload switch (modulo) { case 1: sum += (i >>> 16) & 0xFF00; // if (Debug.enabled) // Debug.println("Checksum: " + Debug.intToHexString(sum += // (i >>> 16) & 0xFF00) + " (ofs: " + ofs + ")", // Debug.DBG_TCP); break; case 2: sum += (i >>> 16) & 0xFFFF; // if (Debug.enabled) // Debug.println("Checksum: " + Debug.intToHexString(sum += // (i >>> 16) & 0xFFFF) + " (ofs: " + ofs + ")", // Debug.DBG_TCP); break; case 3: sum += i & 0xFF00; sum += (i >>> 16) & 0xFFFF; // if (Debug.enabled) // Debug.println("Checksum: " + Debug.intToHexString(sum += // i & 0xFF00) + " " + Debug.intToHexString(sum += (i >>> // 16) & 0xFFFF) + " (ofs: " + ofs + ")", Debug.DBG_TCP); } } // compute over the pseudo header sum += (IPPacket.getDestAddr(pay) & 0xFFFF) + (IPPacket.getDestAddr(pay) >>> 16); sum += (IPPacket.getSrcAddr(pay) & 0xFFFF) + (IPPacket.getSrcAddr(pay) >>> 16); sum += IPPacket.getProtocol(pay) & 0x00FF; sum += tcpPacketLength & 0xFFFF; while ((sum >> 16) != 0) sum = (sum & 0xffff) + (sum >> 16); sum = (~sum) & 0xffff; // if (Debug.enabled) // Debug.println("Checksum: " + Debug.intToHexString(sum), // Debug.DBG_TCP); return (short) sum; } /** * Check whether the checksum is valid. * * @param pay * The Payload * @return Whether the checksum is valid. */ public static boolean isChecksumValid(Payload pay) { return calculateChecksum(pay) == 0; } /** * Appends the option header to set the Maximum Segmentation Size (MSS) * according to the maximum size of payload buffer. * <p> * <b>Note:</b> This option has to be transmitted only during the initial * connection request! The TCP payload data already written to the Payload * pay will be overwritten! => do it before storing data * * @param pay * The Payload */ public static void setMMS(Payload pay) { byte dofs = getDataOffset(pay); // add the option header (first byte = 2: MSS option, second byte = 4: // number // of bytes for this option, the other two bytes for the actual size) pay.payload[dofs] = 0x02040000 | (StackParameters.TCP_RCV_MAX_SEGMENT_SIZE & 0xFFFF); // and change the data offset setDataOffset(pay, (byte) (dofs + 1)); pay.length += 4; } /** * Returns the length of the payload in octets. This is the size of the IP * packet minus the size of the IP and the TCP headers. * * @param pay * The Payload * @return The number of payload octets */ public static int getDataLength(Payload pay) { return pay.length - ((pay.payload[3] >>> 28) & 0x0F) * 4; } /** * Fills the payload buffer with a given byte array. * * @param pay * The Payload to write to * @param buffer * The data to write * @throws JtcpipException * Data too large */ public static void setData(Payload pay, byte[] buffer) throws JtcpipException { // Could throw an IndexOutOfBounds exception here setData(pay, buffer, 0, (short) buffer.length); } /** * Fills the payload buffer with a given area of a byte array. * * @param pay * The Payload to write to * @param buffer * The data to write * @param firstByte * The start of the data to write * @param count * The length of the data to write * @throws JtcpipException * Data too large */ public static void setData(Payload pay, byte[] buffer, int firstByte, short count) throws JtcpipException { pay.setData(getDataOffset(pay), buffer, firstByte, count); } }