diff options
Diffstat (limited to 'libnetutil/TCPHeader.cc')
-rw-r--r-- | libnetutil/TCPHeader.cc | 936 |
1 files changed, 936 insertions, 0 deletions
diff --git a/libnetutil/TCPHeader.cc b/libnetutil/TCPHeader.cc new file mode 100644 index 0000000..e35d5e1 --- /dev/null +++ b/libnetutil/TCPHeader.cc @@ -0,0 +1,936 @@ +/*************************************************************************** + * TCPHeader.cc -- The TCPHeader Class represents a TCP packet. It * + * contains methods to set the different header fields. These methods * + * tipically perform the necessary error checks and byte order * + * conversions. * + * * + ***********************IMPORTANT NMAP LICENSE TERMS************************ + * + * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap + * Project"). Nmap is also a registered trademark of the Nmap Project. + * + * This program is distributed under the terms of the Nmap Public Source + * License (NPSL). The exact license text applying to a particular Nmap + * release or source code control revision is contained in the LICENSE + * file distributed with that version of Nmap or source code control + * revision. More Nmap copyright/legal information is available from + * https://nmap.org/book/man-legal.html, and further information on the + * NPSL license itself can be found at https://nmap.org/npsl/ . This + * header summarizes some key points from the Nmap license, but is no + * substitute for the actual license text. + * + * Nmap is generally free for end users to download and use themselves, + * including commercial use. It is available from https://nmap.org. + * + * The Nmap license generally prohibits companies from using and + * redistributing Nmap in commercial products, but we sell a special Nmap + * OEM Edition with a more permissive license and special features for + * this purpose. See https://nmap.org/oem/ + * + * If you have received a written Nmap license agreement or contract + * stating terms other than these (such as an Nmap OEM license), you may + * choose to use and redistribute Nmap under those terms instead. + * + * The official Nmap Windows builds include the Npcap software + * (https://npcap.com) for packet capture and transmission. It is under + * separate license terms which forbid redistribution without special + * permission. So the official Nmap Windows builds may not be redistributed + * without special permission (such as an Nmap OEM license). + * + * Source is provided to this software because we believe users have a + * right to know exactly what a program is going to do before they run it. + * This also allows you to audit the software for security holes. + * + * Source code also allows you to port Nmap to new platforms, fix bugs, and add + * new features. You are highly encouraged to submit your changes as a Github PR + * or by email to the dev@nmap.org mailing list for possible incorporation into + * the main distribution. Unless you specify otherwise, it is understood that + * you are offering us very broad rights to use your submissions as described in + * the Nmap Public Source License Contributor Agreement. This is important + * because we fund the project by selling licenses with various terms, and also + * because the inability to relicense code has caused devastating problems for + * other Free Software projects (such as KDE and NASM). + * + * The free version of Nmap is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, + * indemnification and commercial support are all available through the + * Npcap OEM program--see https://nmap.org/oem/ + * + ***************************************************************************/ +/* This code was originally part of the Nping tool. */ + +#include "TCPHeader.h" +#include <assert.h> +/******************************************************************************/ +/* CONTRUCTORS, DESTRUCTORS AND INITIALIZATION METHODS */ +/******************************************************************************/ +TCPHeader::TCPHeader(){ + this->reset(); +} /* End of TCPHeader constructor */ + + +TCPHeader::~TCPHeader(){ + +} /* End of TCPHeader destructor */ + +/** Sets every attribute to its default value */ +void TCPHeader::reset(){ + memset(&this->h, 0, sizeof(nping_tcp_hdr_t)); + this->length=TCP_HEADER_LEN; /* Initial value 20. This will be incremented if options are used */ + this->tcpoptlen=0; + this->setSourcePort(TCP_DEFAULT_SPORT); + this->setDestinationPort(TCP_DEFAULT_DPORT); + this->setSeq(TCP_DEFAULT_SEQ); + this->setAck(TCP_DEFAULT_ACK); + this->setFlags(TCP_DEFAULT_FLAGS); + this->setWindow(TCP_DEFAULT_WIN); + this->setUrgPointer(TCP_DEFAULT_URP); + this->setOffset(); +} /* End of reset() */ + + +/******************************************************************************/ +/* PacketElement:: OVERWRITTEN METHODS */ +/******************************************************************************/ + +/** @warning This method is essential for the superclass getBinaryBuffer() + * method to work. Do NOT change a thing unless you know what you're doing */ +u8 * TCPHeader::getBufferPointer(){ + return (u8*)(&h); +} /* End of getBufferPointer() */ + + +/** Stores supplied packet in the internal buffer so the information + * can be accessed using the standard get & set methods. + * @warning The TCPHeader class is able to hold a maximum of 60 bytes. If the + * supplied buffer is longer than that, only the first 60 bytes will be stored + * in the internal buffer. + * @warning Supplied len MUST be at least 20 bytes (min TCP header length). + * @return OP_SUCCESS on success and OP_FAILURE in case of error */ +int TCPHeader::storeRecvData(const u8 *buf, size_t len){ + if(buf==NULL || len<TCP_HEADER_LEN){ + return OP_FAILURE; + }else{ + int stored_len = MIN((TCP_HEADER_LEN + MAX_TCP_OPTIONS_LEN), len); + this->reset(); /* Re-init the object, just in case the caller had used it already */ + this->length=stored_len; + if(stored_len>TCP_HEADER_LEN) + this->tcpoptlen=stored_len-TCP_HEADER_LEN; + memcpy(&(this->h), buf, stored_len); + } + return OP_SUCCESS; +} /* End of storeRecvData() */ + + +/* Returns a protocol identifier. This is used by packet parsing funtions + * that return linked lists of PacketElement objects, to determine the protocol + * the object represents. */ +int TCPHeader::protocol_id() const { + return HEADER_TYPE_TCP; +} /* End of protocol_id() */ + + +/** Determines if the data stored in the object after an storeRecvData() call + * is valid and safe to use. This mainly checks the length of the data but may + * also test the value of certain protocol fields to ensure their correctness. + * @warning If the information stored in the object has been set through a + * call to storeRecvData(), the object's internal length count may be updated + * if the validation is successful. + * @return the length, in bytes, of the header, if its found to be valid or + * OP_FAILURE (-1) otherwise. */ +int TCPHeader::validate(){ + if(this->getOffset()<5) + return OP_FAILURE; + else if(this->getOffset()*4 > this->length) + return OP_FAILURE; + this->length=this->getOffset()*4; + return this->length; +} /* End of validate() */ + + +/** Prints the contents of the header and calls print() on the next protocol + * header in the chain (if there is any). + * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ +int TCPHeader::print(FILE *output, int detail) const { + char optinfo[256]; + fprintf(output, "TCP["); + fprintf(output, "%d", this->getSourcePort()); + fprintf(output, " >"); + fprintf(output, " %d", this->getDestinationPort()); + fprintf(output, " %s%s%s%s%s%s%s%s", + !this->getSYN() ? "" : "S", + !this->getFIN() ? "" : "F", + !this->getRST() ? "" : "R", + !this->getPSH() ? "" : "P", + !this->getACK() ? "" : "A", + !this->getURG() ? "" : "U", + !this->getECN() ? "" : "E", + !this->getCWR() ? "" : "C" + ); + fprintf(output, " seq=%lu", (long unsigned int)this->getSeq() ); + if(detail>=PRINT_DETAIL_HIGH){ + fprintf(output, " ack=%lu", (long unsigned int)this->getAck() ); + fprintf(output, " off=%d", this->getOffset() ); + fprintf(output, " res=%d", this->h.th_x2); + } + fprintf(output, " win=%hu", this->getWindow() ); + if(detail>=PRINT_DETAIL_MED) + fprintf(output, " csum=0x%04X", ntohs( this->getSum() )); + if(detail>=PRINT_DETAIL_HIGH) + fprintf(output, " urp=%d", this->getUrgPointer() ); + + if(this->tcpoptlen>0 && (this->length >= TCP_HEADER_LEN+this->tcpoptlen) && this->tcpoptlen<=MAX_TCP_OPTIONS_LEN){ + this->__tcppacketoptinfo(this->h.options, this->tcpoptlen, optinfo, sizeof(optinfo)-1); + optinfo[255]='\0'; + fprintf(output, " %s", optinfo); + } + fprintf(output, "]"); + + if(this->next!=NULL){ + print_separator(output, detail); + next->print(output, detail); + } + return OP_SUCCESS; +} /* End of print() */ + + +/* Get an ASCII information about a tcp option which is pointed by + optp, with a length of len. The result is stored in the result + buffer. The result may look like "<mss 1452,sackOK,timestamp + 45848914 0,nop,wscale 7>" */ +void TCPHeader::__tcppacketoptinfo(const u8 *optp, int len, char *result, int bufsize) const { + assert(optp); + assert(result); + char *p, ch; + const u8 *q; + int opcode; + u16 tmpshort; + u32 tmpword1, tmpword2; + unsigned int i=0; + + p = result; + *p = '\0'; + q = optp; + ch = '<'; + + while (len > 0 && bufsize > 2) { + Snprintf(p, bufsize, "%c", ch); + bufsize--; + p++; + opcode = *q++; + if (!opcode) { /* End of List */ + + Snprintf(p, bufsize, "eol"); + bufsize -= strlen(p); + p += strlen(p); + + len--; + + } else if (opcode == 1) { /* No Op */ + Snprintf(p, bufsize, "nop"); + bufsize -= strlen(p); + p += strlen(p); + + len--; + } else if (opcode == 2) { /* MSS */ + if (len < 4) + break; /* MSS has 4 bytes */ + + q++; + memcpy(&tmpshort, q, 2); + + Snprintf(p, bufsize, "mss %u", ntohs(tmpshort)); + bufsize -= strlen(p); + p += strlen(p); + + q += 2; + len -= 4; + } else if (opcode == 3) { /* Window Scale */ + if (len < 3) + break; /* Window Scale option has 3 bytes */ + + q++; + + Snprintf(p, bufsize, "wscale %u", *q); + bufsize -= strlen(p); + p += strlen(p); + + q++; + len -= 3; + } else if (opcode == 4) { /* SACK permitted */ + if (len < 2) + break; /* SACK permitted option has 2 bytes */ + + Snprintf(p, bufsize, "sackOK"); + bufsize -= strlen(p); + p += strlen(p); + + q++; + len -= 2; + } else if (opcode == 5) { /* SACK */ + unsigned sackoptlen = *q; + if ((unsigned) len < sackoptlen) + break; + + /* This would break parsing, so it's best to just give up */ + if (sackoptlen < 2) + break; + + q++; + + if ((sackoptlen - 2) == 0 || ((sackoptlen - 2) % 8 != 0)) { + Snprintf(p, bufsize, "malformed sack"); + bufsize -= strlen(p); + p += strlen(p); + } else { + Snprintf(p, bufsize, "sack %d ", (sackoptlen - 2) / 8); + bufsize -= strlen(p); + p += strlen(p); + for (i = 0; i < sackoptlen - 2; i += 8) { + memcpy(&tmpword1, q + i, 4); + memcpy(&tmpword2, q + i + 4, 4); + Snprintf(p, bufsize, "{%u:%u}", tmpword1, tmpword2); + bufsize -= strlen(p); + p += strlen(p); + } + } + + q += sackoptlen - 2; + len -= sackoptlen; + } else if (opcode == 8) { /* Timestamp */ + if (len < 10) + break; /* Timestamp option has 10 bytes */ + + q++; + memcpy(&tmpword1, q, 4); + memcpy(&tmpword2, q + 4, 4); + + Snprintf(p, bufsize, "timestamp %u %u", ntohl(tmpword1), + ntohl(tmpword2)); + bufsize -= strlen(p); + p += strlen(p); + + q += 8; + len -= 10; + } + + ch = ','; + } + + if (len > 0) { + *result = '\0'; + return; + } + + Snprintf(p, bufsize, ">"); +} + + + +/******************************************************************************/ +/* PROTOCOL-SPECIFIC METHODS */ +/******************************************************************************/ + +/** Sets source port. + * @warning Port must be supplied in host byte order. This method performs + * byte order conversion using htons() */ +int TCPHeader::setSourcePort(u16 p){ + h.th_sport = htons(p); + return OP_SUCCESS; +} /* End of setSourcePort() */ + + +/** Returns source port in HOST byte order */ +u16 TCPHeader::getSourcePort() const { + return ntohs(h.th_sport); +} /* End of getSourcePort() */ + + +/** Sets destination port. + * @warning Port must be supplied in host byte order. This method performs + * byte order conversion using htons() */ +int TCPHeader::setDestinationPort(u16 p){ + h.th_dport = htons(p); + return OP_SUCCESS; +} /* End of setDestinationPort() */ + + +/** Returns destination port in HOST byte order */ +u16 TCPHeader::getDestinationPort() const { + return ntohs(h.th_dport); +} /* End of getDestinationPort() */ + + +/** Sets sequence number. + * @warning Seq number must be supplied in host byte order. This method + * performs byte order conversion using htonl() */ +int TCPHeader::setSeq(u32 p){ + h.th_seq = htonl(p); + return OP_SUCCESS; +} /* End of setSeq() */ + + +/** Returns sequence number in HOST byte order */ +u32 TCPHeader::getSeq() const { + return ntohl(h.th_seq); +} /* End of getSeq() */ + + +/** Sets acknowledgement number. + * @warning ACK number must be supplied in host byte order. This method + * performs byte order conversion using htonl() */ +int TCPHeader::setAck(u32 p){ + h.th_ack = htonl(p); + return OP_SUCCESS; +} /* End of setAck() */ + + +/** Returns ACK number in HOST byte order */ +u32 TCPHeader::getAck() const { + return ntohl(h.th_ack); +} /* End of getAck() */ + + +/* TODO: Test this method. It may not work becuasse th_off is supposed to + * be 4 bits long and arg o is 8. + * UPDATE: It seems to work just fine. However, let's keep this note just + * in case problems arise. */ +int TCPHeader::setOffset(u8 o){ + h.th_off = o; + return OP_SUCCESS; +} /* End of setOffset() */ + + +int TCPHeader::setOffset(){ + h.th_off = 5 + tcpoptlen/4; + return OP_SUCCESS; +} /* End of setOffset() */ + + +/** Returns offset value */ +u8 TCPHeader::getOffset() const { + return h.th_off; +} /* End of getOffset() */ + + +/* Sets the 4-bit reserved field (Note that there are not 4 reserved bits anymore + * as RFC 3540 introduces a new TCP flag, so calling this will overwrite + * the value of such flag. */ +int TCPHeader::setReserved(u8 r){ + h.th_x2 = r; + return OP_SUCCESS; +} + + +u8 TCPHeader::getReserved() const { + return h.th_x2; +} + + +/** Sets TCP flags */ +int TCPHeader::setFlags(u8 f){ + h.th_flags = f; + return OP_SUCCESS; +} /* End of setFlags() */ + + +/** Returns the 8bit flags field of the TCP header */ +u8 TCPHeader::getFlags() const { + return h.th_flags; +} /* End of getFlags() */ + + +/* Returns the 16bit flags field of the TCP header. As RFC 3540 defines a new + * flag (NS), we no longer can store all TCP flags in a single octet, so + * this method returns the flags as a two-octet unsigned integer. */ +u16 TCPHeader::getFlags16() const { + /* Obtain the value of dataoff+reserved+flags in host byte order */ + u16 field=ntohs(*(u16 *)(((u8 *)&this->h)+12)); + /* Erase the contents of the data offset field */ + field = field & 0x0FFF; + return field; +} /* End of getFlags16() */ + + +/** Sets flag CWR + * @return Previous state of the flag */ +bool TCPHeader::setCWR(){ + u8 prev = h.th_flags & TH_CWR; + h.th_flags |= TH_CWR; + return prev; +} /* End of setCWR() */ + + +/** Unsets flag CWR + * @return Previous state of the flag */ +bool TCPHeader::unsetCWR(){ + u8 prev = h.th_flags & TH_CWR; + h.th_flags ^= TH_CWR; + return prev; +} /* End of unsetCWR() */ + + +/** Get CWR flag */ +bool TCPHeader::getCWR() const { + return h.th_flags & TH_CWR; +} /* End of getCWR() */ + + +/** Sets flag ECE/ECN + * @return Previous state of the flag */ +bool TCPHeader::setECE(){ + u8 prev = h.th_flags & TH_ECN; + h.th_flags |= TH_ECN; + return prev; +} /* End of setECE() */ + + +/** Unsets flag ECE/ECN + * @return Previous state of the flag */ +bool TCPHeader::unsetECE(){ + u8 prev = h.th_flags & TH_ECN; + h.th_flags ^= TH_ECN; + return prev; +} /* End of unsetECE() */ + + +/** Get CWR flag */ +bool TCPHeader::getECE() const { + return h.th_flags & TH_ECN; +} /* End of getECE() */ + + +/** Same as setECE() but with a different name since there are two possible + * ways to call this flag + * @return Previous state of the flag */ +bool TCPHeader::setECN(){ + u8 prev = h.th_flags & TH_ECN; + h.th_flags |= TH_ECN; + return prev; +} /* End of setECN() */ + + +/** Unsets flag ECE/ECN + * @return Previous state of the flag */ +bool TCPHeader::unsetECN(){ + u8 prev = h.th_flags & TH_ECN; + h.th_flags ^= TH_ECN; + return prev; +} /* End of unsetECN() */ + + +/** Get ECN flag */ +bool TCPHeader::getECN() const { + return h.th_flags & TH_ECN; +} /* End of getECN() */ + + +/** Sets flag URG + * @return Previous state of the flag */ +bool TCPHeader::setURG(){ + u8 prev = h.th_flags & TH_URG; + h.th_flags |= TH_URG; + return prev; +} /* End of setURG() */ + + +/** Unsets flag URG + * @return Previous state of the flag */ +bool TCPHeader::unsetURG(){ + u8 prev = h.th_flags & TH_URG; + h.th_flags ^= TH_URG; + return prev; +} /* End of unsetURG() */ + + +/** Get URG flag */ +bool TCPHeader::getURG() const { + return h.th_flags & TH_URG; +} /* End of getURG() */ + + +/** Sets flag ACK + * @return Previous state of the flag */ +bool TCPHeader::setACK(){ + u8 prev = h.th_flags & TH_ACK; + h.th_flags |= TH_ACK; + return prev; +} /* End of setACK() */ + + +/** Unsets flag ACK + * @return Previous state of the flag */ +bool TCPHeader::unsetACK(){ + u8 prev = h.th_flags & TH_ACK; + h.th_flags ^= TH_ACK; + return prev; +} /* End of unsetACK() */ + + +/** Get ACK flag */ +bool TCPHeader::getACK() const { + return h.th_flags & TH_ACK; +} /* End of getACK() */ + + +/** Sets flag PSH + * @return Previous state of the flag */ +bool TCPHeader::setPSH(){ + u8 prev = h.th_flags & TH_PSH; + h.th_flags |= TH_PSH; + return prev; +} /* End of setPSH() */ + + +/** Unsets flag PSH + * @return Previous state of the flag */ +bool TCPHeader::unsetPSH(){ + u8 prev = h.th_flags & TH_PSH; + h.th_flags ^= TH_PSH; + return prev; +} /* End of unsetPSH() */ + + +/** Get PSH flag */ +bool TCPHeader::getPSH() const { + return h.th_flags & TH_PSH; +} /* End of getPSH() */ + + +/** Sets flag RST + * @return Previous state of the flag */ +bool TCPHeader::setRST(){ + u8 prev = h.th_flags & TH_RST; + h.th_flags |= TH_RST; + return prev; +} /* End of setRST() */ + + +/** Unsets flag RST + * @return Previous state of the flag */ +bool TCPHeader::unsetRST(){ + u8 prev = h.th_flags & TH_RST; + h.th_flags ^= TH_RST; + return prev; +} /* End of unsetRST() */ + + +/** Get RST flag */ +bool TCPHeader::getRST() const { + return h.th_flags & TH_RST; +} /* End of getRST() */ + + +/** Sets flag SYN + * @return Previous state of the flag */ +bool TCPHeader::setSYN(){ + u8 prev = h.th_flags & TH_SYN; + h.th_flags |= TH_SYN; + return prev; +} /* End of setSYN() */ + + +/** Unsets flag SYN + * @return Previous state of the flag */ +bool TCPHeader::unsetSYN(){ + u8 prev = h.th_flags & TH_SYN; + h.th_flags ^= TH_SYN; + return prev; +} /* End of unsetSYN() */ + + +/** Get SYN flag */ +bool TCPHeader::getSYN() const { + return h.th_flags & TH_SYN; +} /* End of getSYN() */ + + +/** Sets flag FIN + * @return Previous state of the flag */ +bool TCPHeader::setFIN(){ + u8 prev = h.th_flags & TH_FIN; + h.th_flags |= TH_FIN; + return prev; +} /* End of setFIN() */ + + +/** Unsets flag FIN + * @return Previous state of the flag */ +bool TCPHeader::unsetFIN(){ + u8 prev = h.th_flags & TH_FIN; + h.th_flags ^= TH_FIN; + return prev; +} /* End of unsetFIN() */ + + +/** Get FIN flag */ +bool TCPHeader::getFIN() const { + return h.th_flags & TH_FIN; +} /* End of getFIN() */ + + +/** Sets window size. + * @warning Win number must be supplied in host byte order. This method + * performs byte order conversion using htons() */ +int TCPHeader::setWindow(u16 p){ + h.th_win = htons(p); + return OP_SUCCESS; +} /* End of setWindow() */ + + +/** Returns window size in HOST byte order. */ +u16 TCPHeader::getWindow() const { + return ntohs(h.th_win); +} /* End of getWindow() */ + + +/** Sets urgent pointer. + * @warning Pointer must be supplied in host byte order. This method + * performs byte order conversion using htons() */ +int TCPHeader::setUrgPointer(u16 l){ + h.th_urp = htons(l); + return OP_SUCCESS; +} /* End of setUrgPointer() */ + + +/** Returns Urgent Pointer in HOST byte order. */ +u16 TCPHeader::getUrgPointer() const { + return ntohs(h.th_urp); +} /* End of getUrgPointer() */ + + +int TCPHeader::setSum(struct in_addr src, struct in_addr dst){ + int bufflen; + u8 aux[ MAX_TCP_PAYLOAD_LEN ]; + /* FROM: RFC 1323: TCP Extensions for High Performance, March 4, 2009 + * + * "With IP Version 4, the largest amount of TCP data that can be sent in + * a single packet is 65495 bytes (64K - 1 - size of fixed IP and TCP + * headers)". + * + * In theory TCP should not worry about the practical max payload length + * because it is supposed to be independent of the network layer. However, + * since TCP does not have any length field and we need to allocate a + * buffer, we are using that value. (Note htat in UDPHeader.cc we do just + * the opposite, forget about the practical limitation and allow the + * theorical limit for the payload. */ + h.th_sum = 0; + + /* Copy packet contents to a buffer */ + bufflen=dumpToBinaryBuffer(aux, MAX_TCP_PAYLOAD_LEN); + + /* Compute checksum */ + h.th_sum = ipv4_pseudoheader_cksum(&src, &dst, IPPROTO_TCP, bufflen, (char *)aux); + + return OP_SUCCESS; +} /* End of setSum() */ + + +/** @warning Sum is set to supplied value with NO byte ordering conversion + * performed. */ +int TCPHeader::setSum(u16 s){ + h.th_sum = s; + return OP_SUCCESS; +} /* End of setSum() */ + + +int TCPHeader::setSum(){ + this->h.th_sum=0; + this->h.th_sum = this->compute_checksum(); + return OP_SUCCESS; +} /* End of setSum() */ + + +/** Set the TCP checksum field to a random value, which may accidentally + * match the correct checksum */ +int TCPHeader::setSumRandom(){ + h.th_sum=get_random_u16(); + return OP_SUCCESS; +} /* End of setSumRandom() */ + +/** Set the TCP checksum field to a random value. It takes the source and + * destination address to make sure the random generated sum does not + * accidentally match the correct checksum. This function only handles + * IPv4 address. */ +int TCPHeader::setSumRandom(struct in_addr source, struct in_addr destination){ + u16 correct_csum=0; + /* Compute the correct checksum */ + this->setSum(source, destination); + correct_csum=this->getSum(); + /* Generate numbers until one does not match the correct sum */ + while( (h.th_sum=get_random_u16())==correct_csum); + return OP_SUCCESS; +} /* End of setSumRandom() */ + + +/** Returns the TCP checksum field in NETWORK byte order */ +u16 TCPHeader::getSum() const { + return h.th_sum; +} /* End of getSum() */ + + +/* Copies the supplied buffer into the TCP options field. Note that the supplied + * buffer MUST NOT exceed MAX_TCP_OPTIONS_LEN octets and should be a multiple of + * four. If it is not a multiple of four, no error will be returned but the + * behaviour is unspecified. If this method is called passing NULL and zero + * ( t.setOptions(NULL, 0), any existing options are cleared, and the object's + * internal length is updated accordingly. Also, note that a call to setOptions() + * involves an automatic call to setOffset(), which updates the Offset field + * to take into account the new header length. If you need to set a bogus + * data offset, you can do so after calling setOptions(), but not before. + * It returns OP_SUCCESS on success and OP_FAILURE in case of error */ +int TCPHeader::setOptions(const u8 *optsbuff, size_t optslen){ + /* NULL and length=0 means delete existing options */ + if(optsbuff==NULL && optslen==0){ + this->tcpoptlen=0; + this->length=TCP_HEADER_LEN; + memset(this->h.options, 0, MAX_TCP_OPTIONS_LEN); + return OP_SUCCESS; + + /* Make sure params are safe to use */ + }else if(optsbuff==NULL || optslen==0 || optslen>MAX_TCP_OPTIONS_LEN){ + return OP_FAILURE; + + /* Copy supplied buffer into the options field, and update the offset field. */ + }else{ + memcpy(this->h.options, optsbuff, optslen); + this->tcpoptlen=optslen; + this->length=TCP_HEADER_LEN+optslen; + this->setOffset(); + return OP_SUCCESS; + } +} /* End of setOptions() */ + + +/* Returns a pointer to the start of the TCP options field. If the supplied + * "optslen" pointer is not NULL, the length of the options will be stored + * there. */ +const u8 *TCPHeader::getOptions(size_t *optslen) const { + if(optslen!=NULL) + *optslen=this->tcpoptlen; + return this->h.options; +} /* End of getOptions() */ + + +/* Returns the index-th option in the TCP header. On success it returns a + * structure filled with option information. If there is no index-th option, + * it returns a structure with st.value==NULL. Note that this function does + * not perform strict validity checking. It does check that the length claimed + * by the options does not exceed the available buffer but it does not check, + * for example, that the MSS option always contains a length of 4. Also, + * if the returned option type is TCPOPT_EOL or TCPOPT_NOOP, the len field + * would be set to zero and the "value" field should NOT be accessed, as it + * will not contain reliable information. */ +nping_tcp_opt_t TCPHeader::getOption(unsigned int index) const { + nping_tcp_opt_t *curr_opt=NULL; + u8 *curr_pnt=(u8 *)this->h.options; + int bytes_left=this->length - TCP_HEADER_LEN; + assert((this->length - TCP_HEADER_LEN) == this->tcpoptlen); + unsigned int optsfound=0; + nping_tcp_opt_t result; + memset(&result, 0, sizeof(nping_tcp_opt_t)); + + while(bytes_left>0){ + /* Use the opts structure as a template to access current option. It is + * OK to use it because we only access the first two elements. */ + curr_opt=(nping_tcp_opt_t *)curr_pnt; + + /* If we are right in the option that the caller wants, just return it */ + if(optsfound==index){ + result.type=curr_opt->type; + if(result.type==TCPOPT_EOL || result.type==TCPOPT_NOOP) + result.len=1; + else + result.len=curr_opt->len; + result.value=(u8 *)curr_pnt+2; + return result; + } + + /* Otherwise, we have to parse it, so we can skip it and access the next + * option */ + switch(curr_opt->type){ + + /* EOL or NOOP + +-+-+-+-+-+-+-+-+ + | X | + +-+-+-+-+-+-+-+-+ */ + case TCPOPT_EOL: + goto out; + + case TCPOPT_NOOP: + curr_pnt++; /* Skip one octet */ + bytes_left--; + break; + + /* TLV encoded option */ + default: + /* If we don't have as many octets as the option advertises, the + * option is bogus. Return failure. */ + if(bytes_left<curr_opt->len) + return result; + curr_pnt+=curr_opt->len; + bytes_left-=curr_opt->len; + break; + } + optsfound++; + } + +out: + return result; +} + + +/* Returns a textual representation of a TCP Options code */ +const char *TCPHeader::optcode2str(u8 optcode){ + switch(optcode){ + case TCPOPT_EOL: + return "EOL"; + case TCPOPT_NOOP: + return "NOOP"; + case TCPOPT_MSS: + return "MSS"; + case TCPOPT_WSCALE: + return "WScale"; + case TCPOPT_SACKOK: + return "SAckOK"; + case TCPOPT_SACK: + return "SAck"; + case TCPOPT_ECHOREQ: + return "EchoReq"; + case TCPOPT_ECHOREP: + return "EchoRep"; + case TCPOPT_TSTAMP: + return "TStamp"; + case TCPOPT_POCP: + return "POCP"; + case TCPOPT_POSP: + return "POSP"; + case TCPOPT_CC: + return "CC"; + case TCPOPT_CCNEW: + return "CC.NEW"; + case TCPOPT_CCECHO: + return "CC.ECHO"; + case TCPOPT_ALTCSUMREQ: + return "AltSumReq"; + case TCPOPT_ALTCSUMDATA: + return "AltSumData"; + case TCPOPT_MD5: + return "MD5"; + case TCPOPT_SCPS: + return "SCPS"; + case TCPOPT_SNACK: + return "SNAck"; + case TCPOPT_QSRES: + return "QStart"; + case TCPOPT_UTO: + return "UTO"; + case TCPOPT_AO: + return "AO"; + default: + return "Unknown"; + } +} /* End of optcode2str() */ + + |