diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 07:42:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 07:42:04 +0000 |
commit | 0d47952611198ef6b1163f366dc03922d20b1475 (patch) | |
tree | 3d840a3b8c0daef0754707bfb9f5e873b6b1ac13 /libnetutil | |
parent | Initial commit. (diff) | |
download | nmap-0d47952611198ef6b1163f366dc03922d20b1475.tar.xz nmap-0d47952611198ef6b1163f366dc03922d20b1475.zip |
Adding upstream version 7.94+git20230807.3be01efb1+dfsg.upstream/7.94+git20230807.3be01efb1+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libnetutil')
47 files changed, 18961 insertions, 0 deletions
diff --git a/libnetutil/ARPHeader.cc b/libnetutil/ARPHeader.cc new file mode 100644 index 0000000..eeb135e --- /dev/null +++ b/libnetutil/ARPHeader.cc @@ -0,0 +1,313 @@ +/*************************************************************************** + * ARPHeader.cc -- The ARPHeader Class represents an ARP packet. It * + * contains methods to set any header field. In general, these methods do * + * error checkings and byte order conversion. * + * * + ***********************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 "ARPHeader.h" + +/******************************************************************************/ +/* CONTRUCTORS, DESTRUCTORS AND INITIALIZATION METHODS */ +/******************************************************************************/ +ARPHeader::ARPHeader() { + this->reset(); +} /* End of ARPHeader constructor */ + + +ARPHeader::~ARPHeader() { + +} /* End of ARPHeader destructor */ + + +/** Sets every attribute to its default value */ +void ARPHeader::reset(){ + memset (&this->h, 0, sizeof(nping_arp_hdr_t)); + this->length=ARP_HEADER_LEN; +} /* 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 *ARPHeader::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 ARPHeader class is able to hold a maximum of 28 bytes. + * If the supplied buffer is longer than that, only the first 28 bytes will be + * stored in the internal buffer. + * @warning Supplied len MUST be at least 28 bytes (ARP header length). + * @return OP_SUCCESS on success and OP_FAILURE in case of error */ +int ARPHeader::storeRecvData(const u8 *buf, size_t len){ + if(buf==NULL || len<ARP_HEADER_LEN){ + return OP_FAILURE; + }else{ + this->reset(); /* Re-init the object, just in case the caller had used it already */ + this->length=ARP_HEADER_LEN; + memcpy(&(this->h), buf, ARP_HEADER_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 ARPHeader::protocol_id() const { + return HEADER_TYPE_ARP; +} /* 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. + * @return the length, in bytes, of the header, if its found to be valid or + * OP_FAILURE (-1) otherwise. */ +int ARPHeader::validate(){ + if( this->length!=ARP_HEADER_LEN) + return OP_FAILURE; + else + return ARP_HEADER_LEN; +} /* 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 ARPHeader::print(FILE *output, int detail) const { + fprintf(output, "ARP[]"); + if(this->next!=NULL){ + print_separator(output, detail); + next->print(output, detail); + } + return OP_SUCCESS; +} /* End of print() */ + + +/******************************************************************************/ +/* PROTOCOL-SPECIFIC METHODS */ +/******************************************************************************/ + +/** Sets HardwareType. + * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ +int ARPHeader::setHardwareType(u16 val){ + this->h.ar_hrd=htons(val); + return OP_SUCCESS; +} /* End of setHardwareType() */ + + +/** Sets HardwareType to ETHERNET. + * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ +int ARPHeader::setHardwareType(){ + this->h.ar_hrd=htons(HDR_ETH10MB); + return OP_SUCCESS; +} /* End of setHardwareType() */ + + +/** Returns value of attribute h.ar_hrd */ +u16 ARPHeader::getHardwareType(){ + return ntohs(this->h.ar_hrd); +} /* End of getHardwareType() */ + + +/** Sets ProtocolType. + * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ +int ARPHeader::setProtocolType(u16 val){ + this->h.ar_pro=htons(val); + return OP_SUCCESS; +} /* End of setProtocolType() */ + + +/** Sets ProtocolType. + * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ +int ARPHeader::setProtocolType(){ + this->h.ar_pro=htons(0x0800); /* DEFAULT: IPv4 */ + return OP_SUCCESS; +} /* End of setProtocolType() */ + + +/** Returns value of attribute h.ar_pro */ +u16 ARPHeader::getProtocolType(){ + return ntohs(this->h.ar_pro); +} /* End of getProtocolType() */ + + +/** Sets HwAddrLen. + * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ +int ARPHeader::setHwAddrLen(u8 val){ + this->h.ar_hln=val; + return OP_SUCCESS; +} /* End of setHwAddrLen() */ + + +/** Sets HwAddrLen. + * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ +int ARPHeader::setHwAddrLen(){ + this->h.ar_hln=ETH_ADDRESS_LEN; + return OP_SUCCESS; +} /* End of setHwAddrLen() */ + + +/** Returns value of attribute h.ar_hln */ +u8 ARPHeader::getHwAddrLen(){ + return this->h.ar_hln; +} /* End of getHwAddrLen() */ + + +/** Sets ProtoAddrLen. + * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ +int ARPHeader::setProtoAddrLen(u8 val){ + this->h.ar_pln=val; + return OP_SUCCESS; +} /* End of setProtoAddrLen() */ + + +/** Sets ProtoAddrLen. + * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ +int ARPHeader::setProtoAddrLen(){ + this->h.ar_pln=IPv4_ADDRESS_LEN; /* DEFAULT: IPv4 */ + return OP_SUCCESS; +} /* End of setProtoAddrLen() */ + + +/** Returns value of attribute h.ar_pln */ +u8 ARPHeader::getProtoAddrLen(){ + return this->h.ar_pln; +} /* End of getProtoAddrLen() */ + + +/** Sets OpCode. + * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ +int ARPHeader::setOpCode(u16 val){ + this->h.ar_op=htons(val); + return OP_SUCCESS; +} /* End of setOpCode() */ + + +/** Returns value of attribute h.ar_op */ +u16 ARPHeader::getOpCode(){ + return ntohs(this->h.ar_op); +} /* End of getOpCode() */ + + +/** Sets SenderMAC. + * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ +int ARPHeader::setSenderMAC(const u8 * val){ + if(val==NULL) + return OP_FAILURE; + memcpy(this->h.data, val, ETH_ADDRESS_LEN); + return OP_SUCCESS; +} /* End of setSenderMAC() */ + + +/** Returns value of attribute h.ar_sha */ +u8 * ARPHeader::getSenderMAC(){ + return this->h.data; +} /* End of getSenderMAC() */ + + +/** Sets SenderIP. + * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ +int ARPHeader::setSenderIP(struct in_addr val){ + memcpy(this->h.data+6, &val.s_addr, 4); + return OP_SUCCESS; +} /* End of setSenderIP() */ + + +/** Returns value of attribute h.ar_sip */ +u32 ARPHeader::getSenderIP(){ + u32 *p = (u32 *)(this->h.data+6); + return *p; +} /* End of getSenderIP() */ + + +/** Sets TargetMAC. + * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ +int ARPHeader::setTargetMAC(u8 * val){ + if(val==NULL) + return OP_FAILURE; + memcpy(this->h.data+10, val, ETH_ADDRESS_LEN); + return OP_SUCCESS; +} /* End of setTargetMAC() */ + + +/** Returns value of attribute h.ar_tha */ +u8 * ARPHeader::getTargetMAC(){ + return this->h.data+10; +} /* End of getTargetMAC() */ + + +/** Sets TargetIP. + * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ +int ARPHeader::setTargetIP(struct in_addr val){ + memcpy(this->h.data+16, &val.s_addr, 4); + return OP_SUCCESS; +} /* End of setTargetIP() */ + + +/** Returns value of attribute h.ar_tip */ +u32 ARPHeader::getTargetIP(){ + u32 *p = (u32 *)(this->h.data+16); + return *p; +} /* End of getTargetIP() */ diff --git a/libnetutil/ARPHeader.h b/libnetutil/ARPHeader.h new file mode 100644 index 0000000..8386e0b --- /dev/null +++ b/libnetutil/ARPHeader.h @@ -0,0 +1,223 @@ +/*************************************************************************** + * ARPHeader.h -- The ARPHeader Class represents an ARP packet. It * + * contains methods to set any header field. In general, these methods do * + * error checkings and byte order conversion. * + * * + ***********************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. */ + +#ifndef __ARPHEADER_H__ +#define __ARPHEADER_H__ 1 + +#include "NetworkLayerElement.h" + +/* Lengths */ +#define ARP_HEADER_LEN 28 +#define IPv4_ADDRESS_LEN 4 +#define ETH_ADDRESS_LEN 6 + +/* Hardware Types */ +#define HDR_RESERVED 0 /* [RFC5494] */ +#define HDR_ETH10MB 1 /* Ethernet (10Mb) */ +#define HDR_ETH3MB 2 /* Experimental Ethernet (3Mb) */ +#define HDR_AX25 3 /* Amateur Radio AX.25 */ +#define HDR_PRONET_TR 4 /* Proteon ProNET Token Ring */ +#define HDR_CHAOS 5 /* Chaos */ +#define HDR_IEEE802 6 /* IEEE 802 Networks */ +#define HDR_ARCNET 7 /* ARCNET [RFC1201] */ +#define HDR_HYPERCHANNEL 8 /* Hyperchannel */ +#define HDR_LANSTAR 9 /* Lanstar */ +#define HDR_AUTONET 10 /* Autonet Short Address */ +#define HDR_LOCALTALK 11 /* LocalTalk */ +#define HDR_LOCALNET 12 /* LocalNet (IBM PCNet or SYTEK LocalNET) */ +#define HDR_ULTRALINK 13 /* Ultra link */ +#define HDR_SMDS 14 /* SMDS */ +#define HDR_FRAMERELAY 15 /* Frame Relay */ +#define HDR_ATM 16 /* Asynchronous Transmission Mode (ATM) */ +#define HDR_HDLC 17 /* HDLC */ +#define HDR_FIBRE 18 /* Fibre Channel [RFC4338] */ +#define HDR_ATMb 19 /* Asynchronous Transmission Mode (ATM) */ +#define HDR_SERIAL 20 /* Serial Line */ +#define HDR_ATMc 21 /* Asynchronous Transmission Mode [RFC2225] */ +#define HDR_MILSTD 22 /* MIL-STD-188-220 */ +#define HDR_METRICOM 23 /* Metricom */ +#define HDR_IEEE1394 24 /* IEEE 1394.199 */ +#define HDR_MAPOS 25 /* MAPOS [RFC2176] */ +#define HDR_TWINAXIAL 26 /* Twinaxial */ +#define HDR_EUI64 27 /* EUI-64 */ +#define HDR_HIPARP 28 /* HIPARP */ +#define HDR_ISO7816 29 /* IP and ARP over ISO 7816-3 */ +#define HDR_ARPSEC 30 /* ARPSec */ +#define HDR_IPSEC 31 /* IPsec tunnel */ +#define HDR_INFINIBAND 32 /* InfiniBand (TM) */ +#define HDR_TIA102 33 /* TIA-102 Project 25 Common Air Interface */ +#define HDR_WIEGAND 34 /* Wiegand Interface */ +#define HDR_PUREIP 35 /* Pure IP */ +#define HDR_HW_EXP1 36 /* HW_EXP1 [RFC5494] */ +#define HDR_HW_EXP2 37 /* HW_EXP2 [RFC5494] */ + +/* Operation Codes */ +#define OP_ARP_REQUEST 1 /* ARP Request */ +#define OP_ARP_REPLY 2 /* ARP Reply */ +#define OP_RARP_REQUEST 3 /* Reverse ARP Request */ +#define OP_RARP_REPLY 4 /* Reverse ARP Reply */ +#define OP_DRARP_REQUEST 5 /* DRARP-Request */ +#define OP_DRARP_REPLY 6 /* DRARP-Reply */ +#define OP_DRARP_ERROR 7 /* DRARP-Error */ +#define OP_INARP_REQUEST 8 /* InARP-Request */ +#define OP_INARP_REPLY 9 /* InARP-Reply */ +#define OP_ARPNAK 10 /* ARP-NAK */ +#define OP_MARS_REQUEST 11 /* MARS-Request */ +#define OP_MARS_MULTI 12 /* MARS-Multi */ +#define OP_MARS_MSERV 13 /* MARS-MServ */ +#define OP_MARS_JOIN 14 /* MARS-Join */ +#define OP_MARS_LEAVE 15 /* MARS-Leave */ +#define OP_MARS_NAK 16 /* MARS-NAK */ +#define OP_MARS_UNSERV 17 /* MARS-Unserv */ +#define OP_MARS_SJOIN 18 /* MARS-SJoin */ +#define OP_MARS_SLEAVE 19 /* MARS-SLeave */ +#define OP_MARS_GL_REQ 20 /* MARS-Grouplist-Request */ +#define OP_MARS_GL_REP 21 /* MARS-Grouplist-Reply */ +#define OP_MARS_REDIR_MAP 22 /* MARS-Redirect-Map */ +#define OP_MAPOS_UNARP 23 /* MAPOS-UNARP [RFC2176] */ +#define OP_EXP1 24 /* OP_EXP1 [RFC5494] */ +#define OP_EXP2 25 /* OP_EXP2 [RFC5494] */ +#define OP_RESERVED 65535 /* Reserved [RFC5494] */ + + +/* TODO @todo: getTargetIP() and getSenderIP() should either + * return struct in_addr or IPAddress but not u32. */ + +class ARPHeader : public NetworkLayerElement { + + private: + + struct nping_arp_hdr{ + + u16 ar_hrd; /* Hardware Type. */ + u16 ar_pro; /* Protocol Type. */ + u8 ar_hln; /* Hardware Address Length. */ + u8 ar_pln; /* Protocol Address Length. */ + u16 ar_op; /* Operation Code. */ + u8 data[20]; + // Cannot use these because the four-flushing alignment screws up + // everything. I miss ANSI C. + //u8 ar_sha[6]; /* Sender Hardware Address. */ + //u32 ar_sip; /* Sender Protocol Address (IPv4 address). */ + //u8 ar_tha[6]; /* Target Hardware Address. */ + //u32 ar_tip; /* Target Protocol Address (IPv4 address). */ + }__attribute__((__packed__)); + + typedef struct nping_arp_hdr nping_arp_hdr_t; + + nping_arp_hdr_t h; + + public: + + /* Misc */ + ARPHeader(); + ~ARPHeader(); + void reset(); + u8 *getBufferPointer(); + int storeRecvData(const u8 *buf, size_t len); + int protocol_id() const; + int validate(); + int print(FILE *output, int detail) const; + + /* Hardware Type */ + int setHardwareType(u16 t); + int setHardwareType(); + u16 getHardwareType(); + + /* Protocol Type */ + int setProtocolType(u16 t); + int setProtocolType(); + u16 getProtocolType(); + + /* Hardware Address Length */ + int setHwAddrLen(u8 v); + int setHwAddrLen(); + u8 getHwAddrLen(); + + /* Hardware Address Length */ + int setProtoAddrLen(u8 v); + int setProtoAddrLen(); + u8 getProtoAddrLen(); + + /* Operation Code */ + int setOpCode(u16 c); + u16 getOpCode(); + + /* Sender Hardware Address */ + int setSenderMAC(const u8 *m); + u8 *getSenderMAC(); + + /* Sender Protocol address */ + int setSenderIP(struct in_addr i); + u32 getSenderIP(); + + /* Target Hardware Address */ + int setTargetMAC(u8 *m); + u8 *getTargetMAC(); + + /* Target Protocol Address */ + int setTargetIP(struct in_addr i); + u32 getTargetIP(); + +}; /* End of class ARPHeader */ + +#endif /* __ARPHEADER_H__ */ + diff --git a/libnetutil/ApplicationLayerElement.h b/libnetutil/ApplicationLayerElement.h new file mode 100644 index 0000000..f1e7eb5 --- /dev/null +++ b/libnetutil/ApplicationLayerElement.h @@ -0,0 +1,71 @@ +/*************************************************************************** + * ApplicationLayerElement.h -- Class ApplicationLayerElement is a * + * generic class that represents an application layer protocol header or * + * any kind of payload or buffer. Classes like RawData inherit from it. * + * * + ***********************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. */ + +#ifndef APPLICATIONLAYERELEMENT_H +#define APPLICATIONLAYERELEMENT_H 1 + +#include "PacketElement.h" + + +class ApplicationLayerElement : public PacketElement { +}; + +#endif diff --git a/libnetutil/DataLinkLayerElement.h b/libnetutil/DataLinkLayerElement.h new file mode 100644 index 0000000..a0d24a7 --- /dev/null +++ b/libnetutil/DataLinkLayerElement.h @@ -0,0 +1,70 @@ +/*************************************************************************** + * DataLinkLayerElement.h -- Class DataLinkLayerElement is a generic * + * class that represents a data link layer protocol header (and maybe a * + * footer) Classes like EthernetHeader inherit from it. * + * * + ***********************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. */ + +#ifndef DATALINKLAYERELEMENT_H +#define DATALINKLAYERELEMENT_H 1 + +#include "PacketElement.h" + +class DataLinkLayerElement : public PacketElement { +}; + +#endif diff --git a/libnetutil/DestOptsHeader.cc b/libnetutil/DestOptsHeader.cc new file mode 100644 index 0000000..478c3f7 --- /dev/null +++ b/libnetutil/DestOptsHeader.cc @@ -0,0 +1,95 @@ +/*************************************************************************** + * DestOptsHeader.cc -- The DestOptsHeader Class represents an IPv6 * + * Destination Options extension header. * + ***********************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 "DestOptsHeader.h" +#include <assert.h> + +/******************************************************************************/ +/* CONTRUCTORS, DESTRUCTORS AND INITIALIZATION METHODS */ +/******************************************************************************/ +DestOptsHeader::DestOptsHeader() { + this->reset(); +} /* End of DestOptsHeader constructor */ + + +DestOptsHeader::~DestOptsHeader() { + +} /* End of DestOptsHeader destructor */ + + +/** 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 DestOptsHeader::print(FILE *output, int detail) const { + fprintf(output, "DestOpts[%d,%d]", this->h.nh, this->h.len); + // TODO: @todo : Implement this + if(this->next!=NULL){ + print_separator(output, detail); + next->print(output, detail); + } + return OP_SUCCESS; +} /* End of print() */ + + +/* 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 DestOptsHeader::protocol_id() const { + return HEADER_TYPE_IPv6_OPTS; +} /* End of protocol_id() */ diff --git a/libnetutil/DestOptsHeader.h b/libnetutil/DestOptsHeader.h new file mode 100644 index 0000000..131d8d9 --- /dev/null +++ b/libnetutil/DestOptsHeader.h @@ -0,0 +1,86 @@ +/*************************************************************************** + * DestOptsHeader.h -- The DestOptsHeader Class represents an IPv6 * + * Destination Options extension header. * + ***********************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. */ + +#ifndef __DESTOPTS_HEADER_H__ +#define __DESTOPTS_HEADER_H__ 1 + +#include "HopByHopHeader.h" + +class DestOptsHeader : public HopByHopHeader { + + private: + /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Next Header | Hdr Ext Len | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + | | + . . + . Options . + . . + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + // Implemented in HopByHopHeader.h + public: + DestOptsHeader(); + ~DestOptsHeader(); + int print(FILE *output, int detail) const; + int protocol_id() const; + +}; /* End of class DestOptsHeader */ + +#endif diff --git a/libnetutil/EthernetHeader.cc b/libnetutil/EthernetHeader.cc new file mode 100644 index 0000000..5daa3f1 --- /dev/null +++ b/libnetutil/EthernetHeader.cc @@ -0,0 +1,219 @@ +/*************************************************************************** + * EthernetHeader.cc -- The EthernetHeader Class represents an Ethernet * + * header and footer. 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 "EthernetHeader.h" + +/******************************************************************************/ +/* CONTRUCTORS, DESTRUCTORS AND INITIALIZATION METHODS */ +/******************************************************************************/ +EthernetHeader::EthernetHeader(){ + this->reset(); +} /* End of EthernetHeader constructor */ + + +EthernetHeader::~EthernetHeader(){ + +} /* End of EthernetHeader destructor */ + + +/** Sets every attribute to its default value */ +void EthernetHeader::reset(){ + memset(&this->h, 0, sizeof(nping_eth_hdr_t)); + this->length=ETH_HEADER_LEN; +} /* End of reset() */ + + +/** @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 * EthernetHeader::getBufferPointer(){ + return (u8*)(&h); +} /* End of getBufferPointer() */ + + +/******************************************************************************/ +/* PacketElement:: OVERWRITTEN METHODS */ +/******************************************************************************/ + +/** Stores supplied packet in the internal buffer so the information + * can be accessed using the standard get & set methods. + * @warning The EthernetHeader class is able to hold a maximum of 14 bytes. + * If the supplied buffer is longer than that, only the first 14 bytes will be + * stored in the internal buffer. + * @warning Supplied len MUST be at least 14 bytes (Ethernet header length). + * @return OP_SUCCESS on success and OP_FAILURE in case of error */ +int EthernetHeader::storeRecvData(const u8 *buf, size_t len){ + if(buf==NULL || len<ETH_HEADER_LEN){ + return OP_FAILURE; + }else{ + this->reset(); /* Re-init the object, just in case the caller had used it already */ + this->length=ETH_HEADER_LEN; + memcpy(&(this->h), buf, ETH_HEADER_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 EthernetHeader::protocol_id() const { + return HEADER_TYPE_ETHERNET; +} /* 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. + * @return the length, in bytes, of the header, if its found to be valid or + * OP_FAILURE (-1) otherwise. */ +int EthernetHeader::validate(){ + if( this->length!=ETH_HEADER_LEN) + return OP_FAILURE; + else + return ETH_HEADER_LEN; +} /* 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 EthernetHeader::print(FILE *output, int detail) const { + + fprintf(output, "Eth["); + + for(int i=0; i<6; i++){ + fprintf(output, "%02x", this->h.eth_smac[i]); + if(i<5) + fprintf(output, ":"); + } + + fprintf(output, " > "); + + for(int i=0; i<6; i++){ + fprintf(output, "%02x", this->h.eth_dmac[i]); + if(i<5) + fprintf(output, ":"); + } + + if(detail>=PRINT_DETAIL_MED) + fprintf(output, " Type=%04x", this->getEtherType()); + + fprintf(output, "]"); + + if(this->next!=NULL){ + print_separator(output, detail); + next->print(output, detail); + } + return OP_SUCCESS; +} /* End of print() */ + + +/******************************************************************************/ +/* PROTOCOL-SPECIFIC METHODS */ +/******************************************************************************/ + +/** Sets Source MAC address + * @warning Supplied buffer must contain at least 6 bytes */ +int EthernetHeader::setSrcMAC(const u8 *m){ + if(m==NULL) + netutil_fatal("EthernetHeader::setSrcMAC(u8*): NULL value supplied "); + memcpy(h.eth_smac, m, 6); + return OP_SUCCESS; +} /* End of setSrcMAC() */ + + +/** Returns source port in HOST byte order + * @warning Returned pointer points directly to a Class internal buffer. If + * contents are changed, the instance of the class will be affected. */ +const u8* EthernetHeader::getSrcMAC() const { + return this->h.eth_smac; +} /* End of getSrcMAC() */ + + +/** Sets Destination MAC address + * @warning Supplied buffer must contain at least 6 bytes */ +int EthernetHeader::setDstMAC(u8 *m){ + if(m==NULL) + netutil_fatal("EthernetHeader::setDstMAC(u8 *): NULL value supplied "); + memcpy(h.eth_dmac, m, 6); + return OP_SUCCESS; +} /* End of setDstMAC() */ + + + +/** Returns destination port in HOST byte order */ +const u8 *EthernetHeader::getDstMAC() const { + return this->h.eth_dmac; +} /* End of getDstMAC() */ + + +int EthernetHeader::setEtherType(u16 val){ + h.eth_type=htons(val); + return OP_SUCCESS; +} /* End of setEtherType() */ + + +/** Returns destination port in HOST byte order */ +u16 EthernetHeader::getEtherType() const { + return ntohs(this->h.eth_type); +} /* End of getEtherType() */ + diff --git a/libnetutil/EthernetHeader.h b/libnetutil/EthernetHeader.h new file mode 100644 index 0000000..6052461 --- /dev/null +++ b/libnetutil/EthernetHeader.h @@ -0,0 +1,133 @@ +/*************************************************************************** + * EthernetHeader.h -- The EthernetHeader Class represents an Ethernet * + * header and footer. 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. */ + +#ifndef ETHERNETHEADER_H +#define ETHERNETHEADER_H 1 + +#include "DataLinkLayerElement.h" + +/* Ether Types. (From RFC 5342 http://www.rfc-editor.org/rfc/rfc5342.txt) */ +#define ETHTYPE_IPV4 0x0800 /* Internet Protocol Version 4 */ +#define ETHTYPE_ARP 0x0806 /* Address Resolution Protocol */ +#define ETHTYPE_FRAMERELAY 0x0808 /* Frame Relay ARP */ +#define ETHTYPE_PPTP 0x880B /* Point-to-Point Tunneling Protocol */ +#define ETHTYPE_GSMP 0x880C /* General Switch Management Protocol */ +#define ETHTYPE_RARP 0x8035 /* Reverse Address Resolution Protocol */ +#define ETHTYPE_IPV6 0x86DD /* Internet Protocol Version 6 */ +#define ETHTYPE_MPLS 0x8847 /* MPLS */ +#define ETHTYPE_MPS_UAL 0x8848 /* MPLS with upstream-assigned label */ +#define ETHTYPE_MCAP 0x8861 /* Multicast Channel Allocation Protocol */ +#define ETHTYPE_PPPOE_D 0x8863 /* PPP over Ethernet Discovery Stage */ +#define ETHTYPE_PPOE_S 0x8864 /* PPP over Ethernet Session Stage */ +#define ETHTYPE_CTAG 0x8100 /* Customer VLAN Tag Type */ +#define ETHTYPE_EPON 0x8808 /* Ethernet Passive Optical Network */ +#define ETHTYPE_PBNAC 0x888E /* Port-based network access control */ +#define ETHTYPE_STAG 0x88A8 /* Service VLAN tag identifier */ +#define ETHTYPE_ETHEXP1 0x88B5 /* Local Experimental Ethertype */ +#define ETHTYPE_ETHEXP2 0x88B6 /* Local Experimental Ethertype */ +#define ETHTYPE_ETHOUI 0x88B7 /* OUI Extended Ethertype */ +#define ETHTYPE_PREAUTH 0x88C7 /* Pre-Authentication */ +#define ETHTYPE_LLDP 0x88CC /* Link Layer Discovery Protocol (LLDP) */ +#define ETHTYPE_MACSEC 0x88E5 /* Media Access Control Security */ +#define ETHTYPE_MVRP 0x88F5 /* Multiple VLAN Registration Protocol */ +#define ETHTYPE_MMRP 0x88F6 /* Multiple Multicast Registration Protocol */ +#define ETHTYPE_FRRR 0x890D /* Fast Roaming Remote Request */ + +#define ETH_HEADER_LEN 14 + +class EthernetHeader : public DataLinkLayerElement { + + private: + + struct nping_eth_hdr{ + u8 eth_dmac[6]; + u8 eth_smac[6]; + u16 eth_type; + }__attribute__((__packed__)); + + typedef struct nping_eth_hdr nping_eth_hdr_t; + + nping_eth_hdr_t h; + + public: + + EthernetHeader(); + ~EthernetHeader(); + void reset(); + u8 *getBufferPointer(); + int storeRecvData(const u8 *buf, size_t len); + int protocol_id() const; + int validate(); + int print(FILE *output, int detail) const; + + int setSrcMAC(const u8 *m); + const u8 *getSrcMAC() const; + + int setDstMAC(u8 *m); + const u8 *getDstMAC() const; + + int setEtherType(u16 val); + u16 getEtherType() const; + +}; + +#endif diff --git a/libnetutil/FragmentHeader.cc b/libnetutil/FragmentHeader.cc new file mode 100644 index 0000000..f89e00c --- /dev/null +++ b/libnetutil/FragmentHeader.cc @@ -0,0 +1,212 @@ +/*************************************************************************** + * FragmentHeader.cc -- The FragmentHeader Class represents an IPv6 * + * Hop-by-Hop extension header. * + ***********************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 "FragmentHeader.h" +#include <assert.h> + +/******************************************************************************/ +/* CONTRUCTORS, DESTRUCTORS AND INITIALIZATION METHODS */ +/******************************************************************************/ +FragmentHeader::FragmentHeader() { + this->reset(); +} /* End of FragmentHeader constructor */ + + +FragmentHeader::~FragmentHeader() { + +} /* End of FragmentHeader destructor */ + + +/** Sets every attribute to its default value */ +void FragmentHeader::reset(){ + memset(&this->h, 0, sizeof(nping_ipv6_ext_fragment_hdr_t)); + this->length=8; +} /* 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 *FragmentHeader::getBufferPointer(){ + return (u8*)(&this->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 FragmentHeader class is able to hold a maximum of + * sizeof(nping_icmpv6_hdr_t) bytes. If the supplied buffer is longer than + * that, only the first 1508 bytes will be stored in the internal buffer. + * @warning Supplied len MUST be at least 8 bytes (min ICMPv6 header length). + * @return OP_SUCCESS on success and OP_FAILURE in case of error */ +int FragmentHeader::storeRecvData(const u8 *buf, size_t len){ + if(buf==NULL || len<FRAGMENT_HEADER_LEN){ + this->length=0; + return OP_FAILURE; + }else{ + int stored_len = MIN(FRAGMENT_HEADER_LEN, len); + this->reset(); /* Re-init the object, just in case the caller had used it already */ + this->length=stored_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 FragmentHeader::protocol_id() const { + return HEADER_TYPE_IPv6_FRAG; +} /* 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. + * @return the length, in bytes, of the header, if its found to be valid or + * OP_FAILURE (-1) otherwise. */ +int FragmentHeader::validate(){ + /* Check the object's length makes sense*/ + if(this->length != FRAGMENT_HEADER_LEN){ + return OP_FAILURE; + } + /* There is not much to check for here, since header fields may take any + * value. We could certainly check the NextHeader value, but let's leave + * that for the class user. */ + 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 FragmentHeader::print(FILE *output, int detail) const { + fprintf(output, "Fragment[%d, %d]", this->h.nh, this->h.id); + // TODO: @todo : Implement this + if(this->next!=NULL){ + print_separator(output, detail); + next->print(output, detail); + } + return OP_SUCCESS; +} /* End of print() */ + + +/******************************************************************************/ +/* PROTOCOL-SPECIFIC METHODS */ +/******************************************************************************/ + +/** Set Next Header field */ +int FragmentHeader::setNextHeader(u8 val){ + this->h.nh = val; + return OP_SUCCESS; +} /* End of setNextHeader() */ + + +/** Returns next header id */ +u8 FragmentHeader::getNextHeader(){ + return this->h.nh; +} /* End of getNextHeader() */ + + +/** Set Offset field */ +int FragmentHeader::setOffset(u16 val){ + this->h.off_res_flag[0]=(u8)(val >> 8); + this->h.off_res_flag[1]=(u8)((this->h.off_res_flag[1] & 0x7) | (val & ~0x7)); + return OP_SUCCESS; +} /* End of setOffset() */ + + +/** Returns fragment offset */ +u16 FragmentHeader::getOffset(){ + return ((this->h.off_res_flag[0] << 8) + this->h.off_res_flag[1]) & 0xfff8; +} /* End of getOffset() */ + + +/* Sets the "More Fragments" flag. */ +int FragmentHeader::setM(bool m_flag){ + if(m_flag) + this->h.off_res_flag[1]= (u8)((this->h.off_res_flag[1] & ~0x01) | 0x01); + else + this->h.off_res_flag[1]= (u8)((this->h.off_res_flag[1] & ~0x1)); + return OP_SUCCESS; +} /* End of setM() */ + + +/* Returns true if the "More Fragments" flag is set; false otherwise. */ +bool FragmentHeader::getM(){ + return (this->h.off_res_flag[1] & 0x01); +} /* End of getM() */ + + +/** Set the fragment identifier */ +int FragmentHeader::setIdentification(u32 val){ + this->h.id=htonl(val); + return OP_SUCCESS; +} /* End of setIdentification() */ + + +/** Returns the fragment identifier*/ +u32 FragmentHeader::getIdentification(){ + return ntohl(this->h.id); +} /* End of getIdentification.() */ diff --git a/libnetutil/FragmentHeader.h b/libnetutil/FragmentHeader.h new file mode 100644 index 0000000..9f4592f --- /dev/null +++ b/libnetutil/FragmentHeader.h @@ -0,0 +1,113 @@ +/*************************************************************************** + * FragmentHeader.h -- The FragmentHeader Class represents an IPv6 * + * Hop-by-Hop extension header. * + ***********************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. */ + +#ifndef __FRAGMENT_HEADER_H__ +#define __FRAGMENT_HEADER_H__ 1 + +#include "IPv6ExtensionHeader.h" + +#define FRAGMENT_HEADER_LEN 8 + + +class FragmentHeader : public IPv6ExtensionHeader { + + private: + + /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Next Header | Reserved | Fragment Offset |Res|M| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Identification | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + struct nping_ipv6_ext_fragment_hdr{ + u8 nh; + u8 res1; + u8 off_res_flag[2]; + u32 id; + }__attribute__((__packed__)); + typedef struct nping_ipv6_ext_fragment_hdr nping_ipv6_ext_fragment_hdr_t; + + nping_ipv6_ext_fragment_hdr_t h; + + public: + FragmentHeader(); + ~FragmentHeader(); + void reset(); + u8 *getBufferPointer(); + int storeRecvData(const u8 *buf, size_t len); + int protocol_id() const; + int validate(); + int print(FILE *output, int detail) const; + + /* Protocol specific methods */ + int setNextHeader(u8 val); + u8 getNextHeader(); + + int setOffset(u16 val); + u16 getOffset(); + + int setM(bool m_flag); + bool getM(); + + int setIdentification(u32 val); + u32 getIdentification(); + + +}; /* End of class FragmentHeader */ + +#endif diff --git a/libnetutil/HopByHopHeader.cc b/libnetutil/HopByHopHeader.cc new file mode 100644 index 0000000..b0e4467 --- /dev/null +++ b/libnetutil/HopByHopHeader.cc @@ -0,0 +1,388 @@ +/*************************************************************************** + * HopByHopHeader.cc -- The HopByHopHeader Class represents an IPv6 * + * Hop-by-Hop extension header. * + ***********************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 "HopByHopHeader.h" +#include <assert.h> + +/******************************************************************************/ +/* CONTRUCTORS, DESTRUCTORS AND INITIALIZATION METHODS */ +/******************************************************************************/ +HopByHopHeader::HopByHopHeader() { + this->reset(); +} /* End of HopByHopHeader constructor */ + + +HopByHopHeader::~HopByHopHeader() { + +} /* End of HopByHopHeader destructor */ + + +/** Sets every attribute to its default value */ +void HopByHopHeader::reset(){ + memset(&this->h, 0, sizeof(nping_ipv6_ext_hopbyhop_hdr_t)); + curr_option=(u8*)this->h.options; + this->length=2; + this->addOption(EXTOPT_PADN, 4, (const u8*)"\x00\x00\x00\x00"); +} /* 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 *HopByHopHeader::getBufferPointer(){ + return (u8*)(&this->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 HopByHopHeader class is able to hold a maximum of + * sizeof(nping_icmpv6_hdr_t) bytes. If the supplied buffer is longer than + * that, only the first 1508 bytes will be stored in the internal buffer. + * @warning Supplied len MUST be at least 8 bytes (min ICMPv6 header length). + * @return OP_SUCCESS on success and OP_FAILURE in case of error */ +int HopByHopHeader::storeRecvData(const u8 *buf, size_t len){ + if(buf==NULL || len<HOPBYHOP_MIN_HEADER_LEN){ + this->length=0; + return OP_FAILURE; + }else{ + /* Store the first 4 bytes, so we can access the HdrExtLen field. */ + memcpy(&(this->h), buf, 4); + + /* Check that the HdrExtLen field makes sense: + * 1) Check that it carries as many octets as it claims + * 2) Check that we don't exceed our internal storage space. */ + // h.len cannot exceed 0xff, so max is (0xff+1)*8, but HOPBYHOP_MAX_HEADER_LEN is 8 + 0x100*8 + if( ((unsigned int)(this->h.len+1))*8 > len){ + this->length=0; + return OP_FAILURE; + }else{ + int mylen=(this->h.len+1)*8; + this->reset(); + this->length=mylen; + memcpy(&(this->h), buf, this->length); + return OP_SUCCESS; + } + } + return OP_FAILURE; +} /* 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 HopByHopHeader::protocol_id() const { + return HEADER_TYPE_IPv6_HOPOPT; +} /* 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. + * @return the length, in bytes, of the header, if its found to be valid or + * OP_FAILURE (-1) otherwise. */ +int HopByHopHeader::validate(){ + nping_ipv6_ext_hopbyhop_opt_t *curr_opt=NULL; + u8 *curr_pnt=(u8 *)this->h.options; + int bytes_left=this->length-2; + + /* Check the object's length makes sense*/ + if(this->length%8!=0 || this->length < HOPBYHOP_MIN_HEADER_LEN || this->length > HOPBYHOP_MAX_HEADER_LEN){ + return OP_FAILURE; + } + /* Check the header's length field. It should match the object's length */ + if( (this->h.len+1)*8 != this->length){ + return OP_FAILURE; + } + + /* Now validate the TLV-encoded options. */ + while(bytes_left>0){ + /* Use the opts structure as a template to access current option */ + curr_opt=(nping_ipv6_ext_hopbyhop_opt_t *)curr_pnt; + + /* Let's see what we have. */ + switch(curr_opt->type){ + + /* Pad1 + +-+-+-+-+-+-+-+-+ + | 0 | + +-+-+-+-+-+-+-+-+ */ + case EXTOPT_PAD1: + curr_pnt++; /* Skip one octet */ + bytes_left++; + break; + + /* PadN + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - - - - - - - - + | 1 | Padding Len | Padding + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - - - - - - - - */ + case EXTOPT_PADN: + /* Check we have as many octets as the option advertises */ + if(bytes_left<2+curr_opt->len) + return OP_FAILURE; + curr_pnt+=2+curr_opt->len; + bytes_left-=2+curr_opt->len; + break; + + /* Jumbo Payload Option (RFC 2675). + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Option Type | Opt Data Len | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Jumbo Payload Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + case EXTOPT_JUMBO: + /* Jumbo has a fixed length of 4 octets (plus 2). */ + if(curr_opt->len!=4) + return OP_FAILURE; + /* Check if we actually have all the octets */ + if(bytes_left<2+4) + return OP_FAILURE; + curr_pnt+=6; + bytes_left-=6; + break; + + /* Tunnel Encapsulation limit (RFC 2473). + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Option Type | 1 | Tun Encap Lim | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + case EXTOPT_TUNENCAPLIM: + /* This one also has a fixed length. */ + if(curr_opt->len!=1) + return OP_FAILURE; + /* Check if we actually have all the octets */ + if(bytes_left<2+1) + return OP_FAILURE; + curr_pnt+=3; + bytes_left-=3; + break; + + /* Router Alert (RFC 2711). + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Option Type | 2 | Value (2 octets) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + case EXTOPT_ROUTERALERT: + /* Fixed length (two octets)*/ + if(curr_opt->len!=2) + return OP_FAILURE; + /* Check that we actually have all the octets */ + if(bytes_left<2+2) + return OP_FAILURE; + curr_pnt+=4; + bytes_left-=4; + break; + + /* Quick-Start (RFC 4782). + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Option | Length=6 | Func. | Rate | Not Used | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | QS Nonce | R | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + case EXTOPT_QUICKSTART: + /* Fixed length (two octets)*/ + if(curr_opt->len!=6) + return OP_FAILURE; + /* Check that we actually have all the octets */ + if(bytes_left<2+6) + return OP_FAILURE; + curr_pnt+=8; + bytes_left-=8; + break; + + /* CALIPSO (RFC 5570). + +----------------------------+ + | Option Type | Option Length| + +-------------+---------------+-------------+--------------+ + | CALIPSO Domain of Interpretation | + +-------------+---------------+-------------+--------------+ + | Cmpt Length | Sens Level | Checksum (CRC-16) | + +-------------+---------------+-------------+--------------+ + | Compartment Bitmap (Optional; variable length) | + +-------------+---------------+-------------+--------------+ */ + case EXTOPT_CALIPSO: + /* The length of the CALIPSO option is variable because the + * Compartment Bitmap is not mandatory. However, the length + * must be at least 8. */ + if(curr_opt->len<8) + return OP_FAILURE; + /* Check that we actually have all the octets */ + if(bytes_left<2+curr_opt->len) + return OP_FAILURE; + curr_pnt+=2+curr_opt->len; + bytes_left-=2+curr_opt->len; + break; + + + /* Home Address (RFC 6275). + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Option Type | Option Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + + + | | + + Home Address + + | | + + + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+*/ + case EXTOPT_HOMEADDR: + /* Fixed length of 16 */ + if(curr_opt->len!=16) + return OP_FAILURE; + /* Check if we actually have all the octets */ + if(bytes_left<2+16) + return OP_FAILURE; + curr_pnt+=18; + bytes_left-=18; + break; + + /* Option Type Unknown */ + default: + /* If we don't know the option, we can still try to validate it, + * checking if the OptionLength contains something reasonable. */ + /* Fixed length of 16 */ + if(bytes_left<2+curr_opt->len) + return OP_FAILURE; + curr_pnt+=2+curr_opt->len; + bytes_left-=2+curr_opt->len; + break; + } + } + 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 HopByHopHeader::print(FILE *output, int detail) const { + fprintf(output, "HopByHop[%d,%d]", this->h.nh, this->h.len); + // TODO: @todo : Implement this + if(this->next!=NULL){ + print_separator(output, detail); + next->print(output, detail); + } + return OP_SUCCESS; +} /* End of print() */ + + +/******************************************************************************/ +/* PROTOCOL-SPECIFIC METHODS */ +/******************************************************************************/ + +/** Set Next Header field */ +int HopByHopHeader::setNextHeader(u8 val){ + this->h.nh = val; + return OP_SUCCESS; +} /* End of setNextHeader() */ + + +/** Returns next header id */ +u8 HopByHopHeader::getNextHeader(){ + return this->h.nh; +} /* End of getNextHeader() */ + + +/* Add TLV encoded option */ +int HopByHopHeader::addOption(u8 type, u8 len, const u8 *data){ + /* Make sure we don't screw up due to buffer length issues */ + if(data==NULL) + return OP_FAILURE; + if ( (this->length+len+2) > HOPBYHOP_MAX_HEADER_LEN ) /* No space for more */ + return OP_FAILURE; + + /* Store the option */ + curr_option[0]=type; + curr_option[1]=len; + memcpy(curr_option+2, data, len); + /* Update internal option offset and object's length */ + curr_option+=(len+2); + this->length+=(len+2); + this->addPadding(); + return OP_SUCCESS; + +} /* End of addOption() */ + + +/* If the current length of the extension header is not a multiple of 8 octets, + * this method adds the necessary padding (either PadN or Pad1 options)*/ +int HopByHopHeader::addPadding(){ + u8 zeroes[8]={0,0,0,0,0,0,0,0}; + // required_octets in range [0,7] + int required_octets=(8 - (this->length % 8)) % 8; + + /* Make sure we have enough space for the padding. */ + if ( (this->length+required_octets) > HOPBYHOP_MAX_HEADER_LEN ) + return OP_FAILURE; + + /* Insert Pad1 or PadN to fill the necessary octets */ + if (required_octets == 1) { + curr_option[0]=EXTOPT_PAD1; + curr_option++; + this->length++; + } + else if (required_octets > 0) { + this->addOption(EXTOPT_PADN, required_octets-2, zeroes ); + } + assert(this->length%8==0); + this->h.len=(this->length/8)-1; + return OP_SUCCESS; +} /* End of addPadding() */ diff --git a/libnetutil/HopByHopHeader.h b/libnetutil/HopByHopHeader.h new file mode 100644 index 0000000..9fdfd45 --- /dev/null +++ b/libnetutil/HopByHopHeader.h @@ -0,0 +1,122 @@ +/*************************************************************************** + * HopByHopHeader.h -- The HopByHopHeader Class represents an IPv6 * + * Hop-by-Hop extension header. * + ***********************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. */ + +#ifndef __HOP_BY_HOP_HEADER_H__ +#define __HOP_BY_HOP_HEADER_H__ 1 + +#include "IPv6ExtensionHeader.h" + +#define HOP_BY_HOP_MAX_OPTIONS_LEN 256*8 +#define HOPBYHOP_MIN_HEADER_LEN 8 +#define HOPBYHOP_MAX_HEADER_LEN (HOPBYHOP_MIN_HEADER_LEN + HOP_BY_HOP_MAX_OPTIONS_LEN) +#define HOPBYHOP_MAX_OPTION_LEN 256 + +class HopByHopHeader : public IPv6ExtensionHeader { + + protected: + /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Next Header | Hdr Ext Len | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + | | + . . + . Options . + . . + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + struct nping_ipv6_ext_hopbyhop_hdr{ + u8 nh; + u8 len; + u8 options[HOP_BY_HOP_MAX_OPTIONS_LEN]; + }__attribute__((__packed__)); + typedef struct nping_ipv6_ext_hopbyhop_hdr nping_ipv6_ext_hopbyhop_hdr_t; + + + /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - - - - - - - - + | Option Type | Opt Data Len | Option Data + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - - - - - - - - */ + struct nping_ipv6_ext_hopbyhop_opt{ + u8 type; + u8 len; + u8 data[HOPBYHOP_MAX_OPTION_LEN]; + }__attribute__((__packed__)); + typedef struct nping_ipv6_ext_hopbyhop_opt nping_ipv6_ext_hopbyhop_opt_t; + + nping_ipv6_ext_hopbyhop_hdr_t h; + u8 *curr_option; + + public: + HopByHopHeader(); + ~HopByHopHeader(); + void reset(); + u8 *getBufferPointer(); + int storeRecvData(const u8 *buf, size_t len); + int protocol_id() const; + int validate(); + int print(FILE *output, int detail) const; + + /* Protocol specific methods */ + int setNextHeader(u8 val); + u8 getNextHeader(); + + int addOption(u8 type, u8 len, const u8 *data); + int addPadding(); + +}; /* End of class HopByHopHeader */ + +#endif diff --git a/libnetutil/ICMPHeader.h b/libnetutil/ICMPHeader.h new file mode 100644 index 0000000..1f5bff4 --- /dev/null +++ b/libnetutil/ICMPHeader.h @@ -0,0 +1,83 @@ +/*************************************************************************** + * ICMPHeader.h -- Class ICMPHeader is a generic class for the ICMP * + * protocol. Its aim is to provide a little bit of abstraction from the * + * underlying ICMP version. Classes like ICMPv4Header or ICMPv6Header * + * inherit from it. * + * * + ***********************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. */ + +#ifndef __ICMPHEADER_H__ +#define __ICMPHEADER_H__ 1 + +#include "PacketElement.h" + +class ICMPHeader : public PacketElement { + + public: + + virtual u8 getType() const = 0; + + virtual int setType(u8 val) = 0; + + virtual u8 getCode() const = 0; + + virtual int setCode(u8 val) = 0; + + virtual bool isError() const = 0; +}; + +#endif /* __ICMPHEADER_H__ */ diff --git a/libnetutil/ICMPv4Header.cc b/libnetutil/ICMPv4Header.cc new file mode 100644 index 0000000..3de406d --- /dev/null +++ b/libnetutil/ICMPv4Header.cc @@ -0,0 +1,1187 @@ +/*************************************************************************** + * ICMPv4Header.cc -- The ICMPv4Header Class represents an ICMP version 4 * + * packet. It contains methods to set any header field. In general, these * + * methods do error checkings and byte order conversion. * + * * + ***********************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 "ICMPv4Header.h" + +/******************************************************************************/ +/* CONTRUCTORS, DESTRUCTORS AND INITIALIZATION METHODS */ +/******************************************************************************/ +ICMPv4Header::ICMPv4Header() { + this->reset(); +} /* End of ICMPv4Header constructor */ + + +ICMPv4Header::~ICMPv4Header() { + +} /* End of ICMPv4Header destructor */ + + +/** Sets every attribute to its default value */ +void ICMPv4Header::reset(){ + memset(&this->h, 0, sizeof(nping_icmpv4_hdr_t)); + h_du = (icmp4_dest_unreach_msg_t *)this->h.data; + h_te = (icmp4_time_exceeded_msg_t *)this->h.data; + h_pp = (icmp4_parameter_problem_msg_t *)this->h.data; + h_sq = (icmp4_source_quench_msg_t *)this->h.data; + h_r = (icmp4_redirect_msg_t *)this->h.data; + h_e = (icmp4_echo_msg_t *)this->h.data; + h_t = (icmp4_timestamp_msg_t *)this->h.data; + h_i = (icmp4_information_msg_t *)this->h.data; + h_ra = (icmp4_router_advert_msg_t *)this->h.data; + h_rs = (icmp4_router_solicit_msg_t *)this->h.data; + h_sf = (icmp4_security_failures_msg_t *)this->h.data; + h_am = (icmp4_address_mask_msg_t *)this->h.data; + h_trc = (icmp4_traceroute_msg_t *)this->h.data; + h_dn = (icmp4_domain_name_request_msg_t *)this->h.data; + h_dnr = (icmp4_domain_name_reply_msg_t *)this->h.data; + this->routeradventries=0; + this->domainnameentries=0; +} /* 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 *ICMPv4Header::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 ICMPv4Header class is able to hold a maximum of 1508 bytes. + * If the supplied buffer is longer than that, only the first 1508 bytes will + * be stored in the internal buffer. + * @warning Supplied len MUST be at least 8 bytes (min ICMPv4 header length). + * @return OP_SUCCESS on success and OP_FAILURE in case of error */ +int ICMPv4Header::storeRecvData(const u8 *buf, size_t len){ + if(buf==NULL || len<ICMP_STD_HEADER_LEN){ + return OP_FAILURE; + }else{ + int stored_len = MIN((ICMP_MAX_PAYLOAD_LEN+4), len); + this->reset(); /* Re-init the object, just in case the caller had used it already */ + this->length=stored_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 ICMPv4Header::protocol_id() const { + return HEADER_TYPE_ICMPv4; +} /* 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. + * @return the length, in bytes, of the header, if its found to be valid or + * OP_FAILURE (-1) otherwise. */ +int ICMPv4Header::validate(){ + int should_have=this->getICMPHeaderLengthFromType( this->getType() ); + if(this->length < should_have){ + return OP_FAILURE; + }else{ + /* WARNING: TODO: @todo This does not work for those messages whose + * length is variable (e.g: router advertisements). */ + return should_have; + } +} /* 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 ICMPv4Header::print(FILE *output, int detail) const { + u8 type=this->getType(); + u8 code=this->getCode(); + char auxstr[64]; + struct in_addr auxaddr; + const char *typestr=this->type2string(type, code); + + fprintf(output, "ICMPv4[%s", typestr); + if(detail>=PRINT_DETAIL_MED) + fprintf(output, " (type=%u/code=%u)", type, code); + + switch(type) { + case ICMP_ECHOREPLY: + case ICMP_ECHO: + case ICMP_INFO: + case ICMP_INFOREPLY: + fprintf(output, " id=%u seq=%u", this->getIdentifier(), this->getSequence()); + break; + + case ICMP_UNREACH: + case ICMP_SOURCEQUENCH: + case ICMP_ROUTERSOLICIT: + if(detail>=PRINT_DETAIL_HIGH) + fprintf(output, " unused=%u", this->getUnused()); + break; + + case ICMP_REDIRECT: + auxaddr=this->getGatewayAddress(); + inet_ntop(AF_INET, &auxaddr, auxstr, sizeof(auxstr)-1); + fprintf(output, " addr=%s", auxstr); + break; + + case ICMP_ROUTERADVERT: + fprintf(output, " addrs=%u addrlen=%u lifetime=%d", + this->getNumAddresses(), + this->getAddrEntrySize(), + this->getLifetime() + ); + break; + + case ICMP_PARAMPROB: + fprintf(output, " pointer=%u", this->getParameterPointer()); + break; + + case ICMP_TSTAMP: + case ICMP_TSTAMPREPLY: + fprintf(output, " id=%u seq=%u", this->getIdentifier(), this->getSequence()); + fprintf(output, " orig=%lu recv=%lu trans=%lu", + (unsigned long)this->getOriginateTimestamp(), + (unsigned long)this->getReceiveTimestamp(), + (unsigned long)this->getTransmitTimestamp() ); + break; + + case ICMP_MASK: + case ICMP_MASKREPLY: + fprintf(output, " id=%u seq=%u", this->getIdentifier(), this->getSequence()); + auxaddr=this->getAddressMask(); + inet_ntop(AF_INET, &auxaddr, auxstr, sizeof(auxstr)-1); + fprintf(output, " mask=%s", auxstr); + break; + + case ICMP_TRACEROUTE: + fprintf(output, " id=%u", this->getIDNumber()); + if(detail>=PRINT_DETAIL_HIGH) + fprintf(output, " unused=%u", this->getUnused()); + if(detail>=PRINT_DETAIL_MED){ + fprintf(output, " outhops=%u", this->getOutboundHopCount() ); + fprintf(output, " rethops=%u", this->getReturnHopCount() ); + } + if(detail>=PRINT_DETAIL_HIGH){ + fprintf(output, " speed=%lu", (unsigned long)this->getOutputLinkSpeed() ); + fprintf(output, " mtu=%lu", (unsigned long)this->getOutputLinkMTU()); + } + break; + + case ICMP_DOMAINNAME: + case ICMP_DOMAINNAMEREPLY: + fprintf(output, " id=%u seq=%u", this->getIdentifier(), this->getSequence()); + /* TODO: print TTL and domain names in replies */ + // UNIMPLEMENTED + break; + + case ICMP_SECURITYFAILURES: + if(detail>=PRINT_DETAIL_HIGH) + fprintf(output, " reserved=%u",this->getReserved()); + fprintf(output, " pointer=%u",this->getSecurityPointer()); + break; + + default: + /* Print nothing */ + break; + } + + if(detail>=PRINT_DETAIL_HIGH) + fprintf(output, " csum=0x%04X", ntohs(this->getSum())); + fprintf(output, "]"); + if(this->next!=NULL){ + print_separator(output, detail); + next->print(output, detail); + } + return OP_SUCCESS; +} /* End of print() */ + + +/******************************************************************************/ +/* PROTOCOL-SPECIFIC METHODS */ +/******************************************************************************/ + +/* ICMPv4 common fields *****************************************************/ +int ICMPv4Header::setType(u8 val){ + h.type = val; + length = getICMPHeaderLengthFromType( val ); + return OP_SUCCESS; +} /* End of setType() */ + + +/** @warning Returned value is in HOST byte order */ +u8 ICMPv4Header::getType() const { + return h.type; +} /* End of getType() */ + +/** Returns true if the supplied type is an RFC compliant type */ +bool ICMPv4Header::validateType(u8 val){ + switch( val ){ + case ICMP_ECHOREPLY: + case ICMP_UNREACH: + case ICMP_SOURCEQUENCH: + case ICMP_REDIRECT: + case ICMP_ECHO: + case ICMP_ROUTERADVERT: + case ICMP_ROUTERSOLICIT: + case ICMP_TIMXCEED: + case ICMP_PARAMPROB: + case ICMP_TSTAMP: + case ICMP_TSTAMPREPLY: + case ICMP_INFO: + case ICMP_INFOREPLY: + case ICMP_MASK: + case ICMP_MASKREPLY: + case ICMP_TRACEROUTE: + case ICMP_DOMAINNAME: + case ICMP_DOMAINNAMEREPLY: + return true; + break; + + default: + return false; + break; + } + return false; +} /* End of validateType() */ + + +/** Returns true if the type fields contains an RFC compliant ICMP message + * type. */ +bool ICMPv4Header::validateType(){ + return validateType( this->h.type ); +} /* End of validateType() */ + + +/** Set ICMP code field */ +int ICMPv4Header::setCode(u8 val){ + h.code = val; + return OP_SUCCESS; +} /* End of setCode() */ + + +/** @warning Returned value is in HOST byte order */ +u8 ICMPv4Header::getCode() const { + return h.code; +} /* End of getCode() */ + + +/** Given an ICMP Type and a code, determines whether the code corresponds to + * a RFC compliant code (eg: code 0x03 for "port unreachable" in ICMP + * Unreachable messages) or just some other bogus code. */ +bool ICMPv4Header::validateCode(u8 type, u8 code){ + switch (type){ + case ICMP_ECHOREPLY: + return (code==0); + break; + + case ICMP_UNREACH: + switch( code ){ + case ICMP_UNREACH_NET: + case ICMP_UNREACH_HOST: + case ICMP_UNREACH_PROTOCOL: + case ICMP_UNREACH_PORT: + case ICMP_UNREACH_NEEDFRAG: + case ICMP_UNREACH_SRCFAIL: + case ICMP_UNREACH_NET_UNKNOWN: + case ICMP_UNREACH_HOST_UNKNOWN: + case ICMP_UNREACH_ISOLATED: + case ICMP_UNREACH_NET_PROHIB: + case ICMP_UNREACH_HOST_PROHIB: + case ICMP_UNREACH_TOSNET: + case ICMP_UNREACH_TOSHOST: + case ICMP_UNREACH_COMM_PROHIB: + case ICMP_UNREACH_HOSTPRECEDENCE: + case ICMP_UNREACH_PRECCUTOFF: + return true; + } + break; + + case ICMP_REDIRECT: + switch( code ){ + case ICMP_REDIRECT_NET: + case ICMP_REDIRECT_HOST: + case ICMP_REDIRECT_TOSNET: + case ICMP_REDIRECT_TOSHOST: + return true; + } + break; + + case ICMP_ROUTERADVERT: + switch( code ){ + case 0: + case ICMP_ROUTERADVERT_MOBILE: + return true; + } + break; + + case ICMP_TIMXCEED: + switch( code ){ + case ICMP_TIMXCEED_INTRANS: + case ICMP_TIMXCEED_REASS: + return true; + } + break; + + case ICMP_PARAMPROB: + switch( code ){ + case ICMM_PARAMPROB_POINTER: + case ICMP_PARAMPROB_OPTABSENT: + case ICMP_PARAMPROB_BADLEN: + return true; + } + break; + + case ICMP_TSTAMP: + case ICMP_TSTAMPREPLY: + case ICMP_INFO: + case ICMP_INFOREPLY: + case ICMP_MASK: + case ICMP_MASKREPLY: + case ICMP_ROUTERSOLICIT: + case ICMP_SOURCEQUENCH: + case ICMP_ECHO: + return (code==0); + break; + + case ICMP_TRACEROUTE: + switch( code ){ + case ICMP_TRACEROUTE_SUCCESS: + case ICMP_TRACEROUTE_DROPPED: + return true; + } + break; + + default: + return false; + break; + } + return false; +} /* End of validateCode() */ + + +/** Computes the ICMP header checksum and sets the checksum field to the right + * value. */ +int ICMPv4Header::setSum(){ + u8 buffer[65535]; + int total_len=0; + h.checksum = 0; + + memcpy(buffer, &h, length); + + if( this->getNextElement() != NULL) + total_len=next->dumpToBinaryBuffer(buffer+length, 65535-length); + total_len+=length; + + h.checksum = in_cksum((unsigned short *)buffer, total_len); + + return OP_SUCCESS; +} /* End of setSum() */ + + +/** @warning Sum is set to supplied value with NO byte ordering conversion + * performed. + * @warning If sum is supplied this way, no error checks are made. Caller is + * responsible for the correctness of the value. */ +int ICMPv4Header::setSum(u16 s){ + h.checksum = s; + return OP_SUCCESS; +} /* End of setSum() */ + + +/** Returns the value of the checksum field. + * @warning The returned value is in NETWORK byte order, no conversion is + * performed */ +u16 ICMPv4Header::getSum() const { + return h.checksum; +} /* End of getSum() */ + + + +/* Dest unreach/Source quench/Time exceeded **********************************/ +/** @warning Supplied value MUST be in host byte order because it will get + * converted by this method using htonl() */ +int ICMPv4Header::setReserved(u32 val){ + u32 aux32=0; + u8 *auxpnt=(u8 *)&aux32; + + switch(this->h.type){ + + case ICMP_UNREACH: + this->h_du->unused=htonl(val); + break; + + case ICMP_TIMXCEED: + this->h_te->unused=htonl(val); + break; + + case ICMP_PARAMPROB: + /* The reserved field in Parameter Problem messages is only + * 24-bits long so we convert the supplied value to big endian and + * use only the 24 least significant bits. */ + aux32=htonl(val); + this->h_pp->unused[0]=auxpnt[1]; + this->h_pp->unused[1]=auxpnt[2]; + this->h_pp->unused[2]=auxpnt[3]; + break; + + case ICMP_SOURCEQUENCH: + this->h_sq->unused=htonl(val); + break; + + case ICMP_ROUTERSOLICIT: + this->h_rs->reserved=htonl(val); + break; + + case ICMP_SECURITYFAILURES: + /* The reserved field in Security failure messages is only + * 16-bits long so we cast it to u16 first (callers are not supposed to + * pass values higher than 2^16) */ + this->h_sf->reserved= htons((u16)val); + break; + + case ICMP_TRACEROUTE: + /* The reserved field in Traceroute messages is only + * 16-bits long so we cast it to u16 first (callers are not supposed to + * pass values higher than 2^16) */ + this->h_trc->unused=htons((u16)val); + break; + + default: + return OP_FAILURE; + break; + } + return OP_SUCCESS; +} /* End of setReserved() */ + + +/** @warning Returned value is in host byte order */ +u32 ICMPv4Header::getReserved() const { + u32 aux32=0; + u8 *auxpnt=(u8 *)&aux32; + + switch(this->h.type){ + + case ICMP_UNREACH: + return ntohl(this->h_du->unused); + break; + + case ICMP_TIMXCEED: + return ntohl(this->h_te->unused); + break; + + case ICMP_PARAMPROB: + /* The unused field in Parameter Problem messages is only + * 24-bits long so we extract the stored value and convert it to host + * byte order. */ + auxpnt[0]=0; + auxpnt[1]=this->h_pp->unused[0]; + auxpnt[2]=this->h_pp->unused[1]; + auxpnt[3]=this->h_pp->unused[2]; + return ntohl(aux32); + break; + + case ICMP_SOURCEQUENCH: + return ntohl(this->h_sq->unused); + break; + + case ICMP_ROUTERSOLICIT: + return ntohl(this->h_rs->reserved); + break; + + case ICMP_SECURITYFAILURES: + /* The unused field in Security Failures messages is only + * 16-bits long so we extract the stored value and cast it to an u32 in + * host byte order */ + return (u32)ntohs(h_sf->reserved); + break; + + case ICMP_TRACEROUTE: + /* The reserved field in Traceroute messages is only + * 16-bits long so we extract the stored value and cast it to an u32 in + * host byte order */ + return (u32)ntohs(h_trc->unused); + break; + + default: + return OP_FAILURE; + break; + } + return OP_SUCCESS; +} /* End of setReserved() */ + + +int ICMPv4Header::setUnused(u32 val){ + return this->setReserved(val); +} /* End of setUnused() */ + + +u32 ICMPv4Header::getUnused() const { + return this->getReserved(); +} /* End of getUnused() */ + + +/* Redirect ******************************************************************/ +/** @warning Supplied IP MUST be in NETWORK byte order */ +int ICMPv4Header::setGatewayAddress(struct in_addr ipaddr){ + h_r->gateway_address=ipaddr; + return OP_SUCCESS; +} /* End of setPreferredRouter() */ + + +struct in_addr ICMPv4Header::getGatewayAddress() const { + return h_r->gateway_address; +} /* End of getPreferredRouter() */ + + + +/* Parameter problem *********************************************************/ +/** Sets pointer value in Parameter Problem messages */ +int ICMPv4Header::setParameterPointer(u8 val){ + h_pp->pointer=val; + return OP_SUCCESS; +} /* End of setParameterPointer() */ + + +/** @warning Returned value is in HOST byte order */ +u8 ICMPv4Header::getParameterPointer() const { + return h_pp->pointer; +} /* End of getParameterPointer() */ + + +/* Router Advertisement ******************************************************/ +int ICMPv4Header::setNumAddresses(u8 val){ + h_ra->num_addrs=val; + return OP_SUCCESS; +} /* End of setNumAddresses() */ + + +u8 ICMPv4Header::getNumAddresses() const { + return h_ra->num_addrs; +} /* End of getNumAddresses() */ + + +int ICMPv4Header::setAddrEntrySize(u8 val){ + h_ra->addr_entry_size=val; + return OP_SUCCESS; +} /* End of setAddrEntrySize() */ + + +/** @warning Returned value is in HOST byte order */ +u8 ICMPv4Header::getAddrEntrySize() const { + return h_ra->addr_entry_size; +} /* End of getAddrEntrySize() */ + + +/** @warning Supplied value MUST be in host byte order because it will get + * converted by this method using htons() */ +int ICMPv4Header::setLifetime(u16 val){ + h_ra->lifetime= htons(val); + return OP_SUCCESS; +} /* End of setLifetime() */ + + +/** @warning Returned value is in HOST byte order */ +u16 ICMPv4Header::getLifetime() const { + return ntohs( h_ra->lifetime ); +} /* End of getLifetime() */ + + +/** @warning Asummes entries have a length of 2*32bits and consist of + * two 32bit values. + * @warning This method automatically updates field "Number of addreses" + * calling this->setNumAddresses(). If you want to place a bogus number + * on such field, setNumAddresses() must be called AFTER any calls to + * addRouterAdvEntry() + * */ +int ICMPv4Header::addRouterAdvEntry(struct in_addr raddr, u32 pref){ + if ( this->routeradventries >= MAX_ROUTER_ADVERT_ENTRIES ) + return OP_FAILURE; + h_ra->adverts[this->routeradventries].router_addr=raddr; + h_ra->adverts[this->routeradventries].preference_level=htonl(pref); + this->routeradventries++; /* Update internal entry count */ + length += 8; /* Update total length of the ICMP packet */ + this->setNumAddresses( this->routeradventries ); /* Update number of addresses */ + return OP_SUCCESS; +} /* End of addRouterAdEntry() */ + + +u8 *ICMPv4Header::getRouterAdvEntries(int *num) const { + if( this->routeradventries <= 0 ) + return NULL; + if (num!=NULL) + *num = this->routeradventries; + return (u8*)h_ra->adverts; +} /* End of getRouterEntries() */ + + +/* Echo/Timestamp/Mask *******************************************************/ +/** @warning Supplied value MUST be in host byte order because it will get + * converted by this method using htons() */ +int ICMPv4Header::setIdentifier(u16 val){ + switch(this->h.type){ + case ICMP_ECHOREPLY: + case ICMP_ECHO: + h_e->identifier=htons(val); + break; + + case ICMP_TSTAMP: + case ICMP_TSTAMPREPLY: + h_t->identifier=htons(val); + break; + + case ICMP_INFO: + case ICMP_INFOREPLY: + h_i->identifier=htons(val); + break; + + case ICMP_MASK: + case ICMP_MASKREPLY: + h_am->identifier=htons(val); + break; + + case ICMP_DOMAINNAME: + h_dn->identifier=htons(val); + break; + + case ICMP_DOMAINNAMEREPLY: + h_dnr->identifier=htons(val); + break; + + default: + return OP_FAILURE; + break; + } + return OP_SUCCESS; +} /* End of setIdentifier() */ + + +/** @warning Returned value is in HOST byte order */ +u16 ICMPv4Header::getIdentifier() const { + switch(this->h.type){ + case ICMP_ECHOREPLY: + case ICMP_ECHO: + return ntohs(h_e->identifier); + break; + + case ICMP_TSTAMP: + case ICMP_TSTAMPREPLY: + return ntohs(h_t->identifier); + break; + + case ICMP_INFO: + case ICMP_INFOREPLY: + return ntohs(h_i->identifier); + break; + + case ICMP_MASK: + case ICMP_MASKREPLY: + return ntohs(h_am->identifier); + break; + + case ICMP_DOMAINNAME: + return ntohs(h_dn->identifier); + break; + + case ICMP_DOMAINNAMEREPLY: + return ntohs(h_dnr->identifier); + break; + + default: + return 0; + break; + } + return 0; +} /* End of getIdentifier() */ + + +/** @warning Supplied value MUST be in host byte order because it will get + * converted by this method using htons() */ +int ICMPv4Header::setSequence(u16 val){ + switch(this->h.type){ + case ICMP_ECHOREPLY: + case ICMP_ECHO: + h_e->sequence=htons(val); + break; + + case ICMP_TSTAMP: + case ICMP_TSTAMPREPLY: + h_t->sequence=htons(val); + break; + + case ICMP_INFO: + case ICMP_INFOREPLY: + h_i->sequence=htons(val); + break; + + case ICMP_MASK: + case ICMP_MASKREPLY: + h_am->sequence=htons(val); + break; + + case ICMP_DOMAINNAME: + h_dn->sequence=htons(val); + break; + + case ICMP_DOMAINNAMEREPLY: + h_dnr->sequence=htons(val); + break; + + default: + return OP_FAILURE; + break; + } + return OP_SUCCESS; +} /* End of setSequence() */ + + +/** @warning Returned value is in HOST byte order */ +u16 ICMPv4Header::getSequence() const { + switch(this->h.type){ + case ICMP_ECHOREPLY: + case ICMP_ECHO: + return ntohs(h_e->sequence); + break; + + case ICMP_TSTAMP: + case ICMP_TSTAMPREPLY: + return ntohs(h_t->sequence); + break; + + case ICMP_INFO: + case ICMP_INFOREPLY: + return ntohs(h_i->sequence); + break; + + case ICMP_MASK: + case ICMP_MASKREPLY: + return ntohs(h_am->sequence); + break; + + case ICMP_DOMAINNAME: + return ntohs(h_dn->sequence); + break; + + case ICMP_DOMAINNAMEREPLY: + return ntohs(h_dnr->sequence); + break; + + default: + return 0; + break; + } + return 0; +} /* End of getSequence() */ + + + +/* Timestamp only ************************************************************/ +/** @warning Supplied value MUST be in host byte order because it will get + * converted by this method using htonl() */ +int ICMPv4Header::setOriginateTimestamp(u32 val){ + h_t->originate_ts=htonl(val); + return OP_SUCCESS; +} /* End of setOriginateTimestamp() */ + + +/** @warning Returned value is in HOST byte order */ +u32 ICMPv4Header::getOriginateTimestamp() const { + return ntohl(h_t->originate_ts); +} /* End of getOriginateTimestamp() */ + + +/** @warning Supplied value MUST be in host byte order because it will get + * converted by this method using htonl() */ +int ICMPv4Header::setReceiveTimestamp(u32 val){ + h_t->receive_ts=htonl(val); + return OP_SUCCESS; +} /* End of setReceiveTimestamp() */ + + +/** @warning Returned value is in HOST byte order */ +u32 ICMPv4Header::getReceiveTimestamp() const { + return ntohl(h_t->receive_ts); +} /* End of getReceiveTimestamp() */ + + +/** @warning Supplied value MUST be in host byte order because it will get + * converted by this method using htonl() */ +int ICMPv4Header::setTransmitTimestamp(u32 val){ + h_t->transmit_ts=htonl(val); + return OP_SUCCESS; +} /* End of setTransmitTimestamp() */ + + +/** @warning Returned value is in HOST byte order */ +u32 ICMPv4Header::getTransmitTimestamp() const { + return ntohl(h_t->transmit_ts); +} /* End of getTransmitTimestamp() */ + + + +/* Mask only ****************************************************************/ +int ICMPv4Header::setAddressMask(struct in_addr ipaddr){ + h_am->address_mask=ipaddr; + return OP_SUCCESS; +} /* End of AddressMask() */ + + +struct in_addr ICMPv4Header::getAddressMask() const { + return h_am->address_mask; +} /* End of getAddressMask() */ + + + +/* Security Failures *********************************************************/ +int ICMPv4Header::setSecurityPointer(u16 val){ + h_sf->pointer=htons(val); + return OP_SUCCESS; +} /* End of setSecurityPointer() */ + + +u16 ICMPv4Header::getSecurityPointer() const { + return ntohs(h_sf->pointer); +} /* End of getSecurityPointer() */ + + + +/* Traceroute ****************************************************************/ +int ICMPv4Header::setIDNumber(u16 val){ + h_trc->id_number = htons(val); + return OP_SUCCESS; +} /* End of setIDNumber() */ + + +u16 ICMPv4Header::getIDNumber() const { + return ntohs(h_trc->id_number); +} /* End of getIDNumber() */ + + +int ICMPv4Header::setOutboundHopCount(u16 val){ + h_trc->outbound_hop_count = htons(val); + return OP_SUCCESS; +} /* End of setOutboundHopCount() */ + + +u16 ICMPv4Header::getOutboundHopCount() const { + return ntohs(h_trc->outbound_hop_count); +} /* End of getOutboundHopCount() */ + + +int ICMPv4Header::setReturnHopCount(u16 val){ + h_trc->return_hop_count = htons(val); + return OP_SUCCESS; +} /* End of seReturnHopCountt() */ + + +u16 ICMPv4Header::getReturnHopCount() const { + return ntohs(h_trc->return_hop_count); +} /* End of getReturnHopCount() */ + + +int ICMPv4Header::setOutputLinkSpeed(u32 val){ + h_trc->output_link_speed = htonl(val); + return OP_SUCCESS; +} /* End of setOutputLinkSpeed() */ + + +u32 ICMPv4Header::getOutputLinkSpeed() const { + return ntohl(h_trc->output_link_speed); +} /* End of getOutputLinkSpeed() */ + + +int ICMPv4Header::setOutputLinkMTU(u32 val){ + h_trc->output_link_mtu = htonl(val); + return OP_SUCCESS; +} /* End of setOutputLinkMTU() */ + + +u32 ICMPv4Header::getOutputLinkMTU() const { + return ntohl(h_trc->output_link_mtu); +} /* End of getOutputLinkMTU() */ + + +/* Miscellaneous *************************************************************/ +/** Returns the standard ICMP header length for the supplied ICMP message type. + * @warning Return value corresponds strictly to the ICMP header, this is, + * the minimum length of the ICMP header, variable length payload is never + * included. For example, an ICMP Router Advertising has a fixed header of 8 + * bytes but then the packet contains a variable number of Router Addresses + * and Preference Levels, so while the length of that ICMP packet is + * 8bytes + ValueInFieldNumberOfAddresses*8, we only return 8 because we + * cannot guarantee that the NumberOfAddresses field has been set before + * the call to this method. Same applies to the rest of types. */ +int ICMPv4Header::getICMPHeaderLengthFromType( u8 type ) const { + + switch( type ){ + + case ICMP_ECHO: + case ICMP_ECHOREPLY: + return 8; /* (+ optional data) */ + break; + + case ICMP_UNREACH: + return 8; /* (+ payload) */ + break; + + case ICMP_SOURCEQUENCH: + return 8; /* (+ payload) */ + break; + + case ICMP_REDIRECT: + return 8; /* (+ payload) */ + break; + + case ICMP_ROUTERADVERT: + return 8; /* (+ value of NumAddr field * 8 ) */ + break; + + case ICMP_ROUTERSOLICIT: + return 8; + break; + + case ICMP_TIMXCEED: + return 8; /* (+ payload) */ + break; + + case ICMP_PARAMPROB: + return 8; /* (+ payload) */ + break; + + case ICMP_TSTAMP: + case ICMP_TSTAMPREPLY: + return 20; + break; + + case ICMP_INFO: + case ICMP_INFOREPLY: + return 8; + break; + + case ICMP_MASK: + case ICMP_MASKREPLY: + return 12; + break; + + case ICMP_TRACEROUTE: + return 20; + break; + + case ICMP_DOMAINNAME: + case ICMP_DOMAINNAMEREPLY: + return 8; + break; + + /* Packets with non RFC-Compliant types will be represented as + an 8-byte ICMP header, just like the types that don't include + additional info (time exceeded, router solicitation, etc) */ + default: + return 8; + break; + } + return 8; +} /* End of getICMPHeaderLengthFromType() */ + + +const char *ICMPv4Header::type2string(int type, int code) const { + switch(type) { + case ICMP_ECHOREPLY: + return "Echo reply"; + break; + + case ICMP_UNREACH: + switch(code) { + case ICMP_UNREACH_NET: return "Network unreachable"; break; + case ICMP_UNREACH_HOST: return "Host unreachable"; break; + case ICMP_UNREACH_PROTOCOL: return "Protocol unreachable"; break; + case ICMP_UNREACH_PORT: return "Port unreachable"; break; + case ICMP_UNREACH_NEEDFRAG: return "Fragmentation required"; break; + case ICMP_UNREACH_SRCFAIL: return "Source route failed"; break; + case ICMP_UNREACH_NET_UNKNOWN: return "Destination network unknown"; break; + case ICMP_UNREACH_HOST_UNKNOWN: return "Destination host unknown"; break; + case ICMP_UNREACH_ISOLATED: return "Source host isolated"; break; + case ICMP_UNREACH_NET_PROHIB: return "Network prohibited"; break; + case ICMP_UNREACH_HOST_PROHIB: return "Host prohibited"; break; + case ICMP_UNREACH_TOSNET: return "Network unreachable for TOS"; break; + case ICMP_UNREACH_TOSHOST: return "Host unreachable for TOS"; break; + case ICMP_UNREACH_COMM_PROHIB: return "Communication prohibited"; break; + case ICMP_UNREACH_HOSTPRECEDENCE: return "Precedence violation"; break; + case ICMP_UNREACH_PRECCUTOFF: return "Precedence cutoff"; break; + default: return "Destination unreachable (unknown code)"; break; + } /* End of ICMP Code switch */ + break; + + case ICMP_SOURCEQUENCH: + return "Source quench"; + break; + + case ICMP_REDIRECT: + switch(code){ + case ICMP_REDIRECT_NET: return "Redirect for network"; break; + case ICMP_REDIRECT_HOST: return "Redirect for host"; break; + case ICMP_REDIRECT_TOSNET: return "Redirect for TOS and network"; break; + case ICMP_REDIRECT_TOSHOST: return "Redirect for TOS and host"; break; + default: return "Redirect (unknown code)"; break; + } + break; + + case ICMP_ECHO: + return "Echo request"; + break; + + case ICMP_ROUTERADVERT: + switch(code){ + case ICMP_ROUTERADVERT_MOBILE: return "Router advertisement (Mobile Agent Only)"; break; + default: return "Router advertisement"; break; + } + break; + + case ICMP_ROUTERSOLICIT: + return "Router solicitation"; + break; + + case ICMP_TIMXCEED: + switch(code){ + case ICMP_TIMXCEED_INTRANS: return "TTL=0 during transit"; break; + case ICMP_TIMXCEED_REASS: return "Reassembly time exceeded"; break; + default: return "TTL exceeded (unknown code)"; break; + } + break; + + case ICMP_PARAMPROB: + switch(code){ + case ICMM_PARAMPROB_POINTER: return "Parameter problem (pointer indicates error)"; break; + case ICMP_PARAMPROB_OPTABSENT: return "Parameter problem (option missing)"; break; + case ICMP_PARAMPROB_BADLEN: return "Parameter problem (bad length)"; break; + default: return "Parameter problem (unknown code)"; break; + } + break; + + case ICMP_TSTAMP: + return "Timestamp request"; + break; + + case ICMP_TSTAMPREPLY: + return "Timestamp reply"; + break; + + case ICMP_INFO: + return "Information request"; + break; + + case ICMP_INFOREPLY: + return "Information reply"; + break; + + case ICMP_MASK: + return "Address mask request "; + break; + + case ICMP_MASKREPLY: + return "Address mask reply"; + break; + + case ICMP_TRACEROUTE: + return "Traceroute"; + break; + + case ICMP_DOMAINNAME: + return "Domain name request"; + break; + + case ICMP_DOMAINNAMEREPLY: + return "Domain name reply"; + break; + + case ICMP_SECURITYFAILURES: + return "Security failures"; + break; + + default: + return "Unknown ICMP type"; + break; + } /* End of ICMP Type switch */ + return "Unknown ICMP type"; +} /* End of type2string() */ + + +/* Returns true if the packet is an ICMPv4 error message. */ +bool ICMPv4Header::isError() const { + switch( this->getType() ){ + case ICMP_UNREACH: + case ICMP_TIMXCEED: + case ICMP_PARAMPROB: + case ICMP_SOURCEQUENCH: + case ICMP_REDIRECT: + case ICMP_SECURITYFAILURES: + return true; + break; + + default: + return false; + break; + } +} /* End of isError() */ diff --git a/libnetutil/ICMPv4Header.h b/libnetutil/ICMPv4Header.h new file mode 100644 index 0000000..01bd6c9 --- /dev/null +++ b/libnetutil/ICMPv4Header.h @@ -0,0 +1,530 @@ +/*************************************************************************** + * ICMPv4Header.h -- The ICMPv4Header Class represents an ICMP version 4 * + * packet. It contains methods to set any header field. In general, these * + * methods do error checkings and byte order conversion. * + * * + ***********************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. */ + +#ifndef ICMPv4HEADER_H +#define ICMPv4HEADER_H 1 + +#include "ICMPHeader.h" + +/* ICMP types and codes. These defines were originally taken from Slirp 1.0 + * source file ip_icmp.h http://slirp.sourceforge.net/ (BSD licensed) and + * then, partially modified for Nping */ +#define ICMP_ECHOREPLY 0 /* Echo reply */ +#define ICMP_UNREACH 3 /* Destination unreachable: */ +#define ICMP_UNREACH_NET 0 /* --> Bad network */ +#define ICMP_UNREACH_HOST 1 /* --> Bad host */ +#define ICMP_UNREACH_PROTOCOL 2 /* --> Bad protocol */ +#define ICMP_UNREACH_PORT 3 /* --> Bad port */ +#define ICMP_UNREACH_NEEDFRAG 4 /* --> DF flag caused pkt drop */ +#define ICMP_UNREACH_SRCFAIL 5 /* --> Source route failed */ +#define ICMP_UNREACH_NET_UNKNOWN 6 /* --> Unknown network */ +#define ICMP_UNREACH_HOST_UNKNOWN 7 /* --> Unknown host */ +#define ICMP_UNREACH_ISOLATED 8 /* --> Source host isolated */ +#define ICMP_UNREACH_NET_PROHIB 9 /* --> Prohibited access */ +#define ICMP_UNREACH_HOST_PROHIB 10 /* --> Prohibited access */ +#define ICMP_UNREACH_TOSNET 11 /* --> Bad TOS for network */ +#define ICMP_UNREACH_TOSHOST 12 /* --> Bad TOS for host */ +#define ICMP_UNREACH_COMM_PROHIB 13 /* --> Prohibited communication */ +#define ICMP_UNREACH_HOSTPRECEDENCE 14 /* --> Host precedence violation */ +#define ICMP_UNREACH_PRECCUTOFF 15 /* --> Precedence cutoff */ +#define ICMP_SOURCEQUENCH 4 /* Source Quench. */ +#define ICMP_REDIRECT 5 /* Redirect: */ +#define ICMP_REDIRECT_NET 0 /* --> For the network */ +#define ICMP_REDIRECT_HOST 1 /* --> For the host */ +#define ICMP_REDIRECT_TOSNET 2 /* --> For the TOS and network */ +#define ICMP_REDIRECT_TOSHOST 3 /* --> For the TOS and host */ +#define ICMP_ECHO 8 /* Echo request */ +#define ICMP_ROUTERADVERT 9 /* Router advertisement */ +#define ICMP_ROUTERADVERT_MOBILE 16 /* Used by mobile IP agents */ +#define ICMP_ROUTERSOLICIT 10 /* Router solicitation */ +#define ICMP_TIMXCEED 11 /* Time exceeded: */ +#define ICMP_TIMXCEED_INTRANS 0 /* --> TTL==0 in transit */ +#define ICMP_TIMXCEED_REASS 1 /* --> TTL==0 in reassembly */ +#define ICMP_PARAMPROB 12 /* Parameter problem */ +#define ICMM_PARAMPROB_POINTER 0 /* --> Pointer shows the problem */ +#define ICMP_PARAMPROB_OPTABSENT 1 /* --> Option missing */ +#define ICMP_PARAMPROB_BADLEN 2 /* --> Bad datagram length */ +#define ICMP_TSTAMP 13 /* Timestamp request */ +#define ICMP_TSTAMPREPLY 14 /* Timestamp reply */ +#define ICMP_INFO 15 /* Information request */ +#define ICMP_INFOREPLY 16 /* Information reply */ +#define ICMP_MASK 17 /* Address mask request */ +#define ICMP_MASKREPLY 18 /* Address mask reply */ +#define ICMP_TRACEROUTE 30 /* Traceroute */ +#define ICMP_TRACEROUTE_SUCCESS 0 /* --> Dgram sent to next router */ +#define ICMP_TRACEROUTE_DROPPED 1 /* --> Dgram was dropped */ +#define ICMP_DOMAINNAME 37 /* Domain name request */ +#define ICMP_DOMAINNAMEREPLY 38 /* Domain name reply */ +#define ICMP_SECURITYFAILURES 40 /* Security failures */ + + +#define ICMP_STD_HEADER_LEN 8 +#define ICMP_MAX_PAYLOAD_LEN 1500 +#define MAX_ROUTER_ADVERT_ENTRIES (((ICMP_MAX_PAYLOAD_LEN-4)/8)-1) + + +class ICMPv4Header : public ICMPHeader { + + private: + + /**********************************************************************/ + /* COMMON ICMPv4 packet HEADER */ + /**********************************************************************/ + /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + Message Body + + | | */ + struct nping_icmpv4_hdr { + u8 type; /* ICMP Message Type */ + u8 code; /* ICMP Message Code */ + u16 checksum; /* Checksum */ + u8 data[ICMP_MAX_PAYLOAD_LEN]; + }__attribute__((__packed__)); + typedef struct nping_icmpv4_hdr nping_icmpv4_hdr_t; + + + /**********************************************************************/ + /* ICMPv4 MESSAGE SPECIFIC HEADERS */ + /**********************************************************************/ + + /* Destination Unreachable Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | unused | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Internet Header + 64 bits of Original Data Datagram | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + struct icmp4_dest_unreach_msg{ + u32 unused; + //u8 original_dgram[?]; + }__attribute__((__packed__)); + typedef struct icmp4_dest_unreach_msg icmp4_dest_unreach_msg_t; + + + /* Time Exceeded Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | unused | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Internet Header + 64 bits of Original Data Datagram | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + struct icmp4_time_exceeded_msg{ + u32 unused; + //u8 original_dgram[?]; + }__attribute__((__packed__)); + typedef struct icmp4_time_exceeded_msg icmp4_time_exceeded_msg_t; + + + /* Parameter Problem Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer | unused | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Internet Header + 64 bits of Original Data Datagram | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + + struct icmp4_parameter_problem_msg{ + u8 pointer; + u8 unused[3]; + //u8 original_dgram[?]; + }__attribute__((__packed__)); + typedef struct icmp4_parameter_problem_msg icmp4_parameter_problem_msg_t; + + + /* Source Quench Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | unused | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Internet Header + 64 bits of Original Data Datagram | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + struct icmp4_source_quench_msg{ + u32 unused; + //u8 original_dgram[?]; + }__attribute__((__packed__)); + typedef struct icmp4_source_quench_msg icmp4_source_quench_msg_t; + + + /* Redirect Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Gateway Internet Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Internet Header + 64 bits of Original Data Datagram | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + struct icmp4_redirect_msg{ + struct in_addr gateway_address; + //u8 original_dgram[?]; + }__attribute__((__packed__)); + typedef struct icmp4_redirect_msg icmp4_redirect_msg_t; + + + /* Echo Request/Reply Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Identifier | Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Data ... + +-+-+-+-+- */ + struct icmp4_echo_msg{ + u16 identifier; + u16 sequence; + //u8 data[?]; + }__attribute__((__packed__)); + typedef struct icmp4_echo_msg icmp4_echo_msg_t; + + + /* Timestamp Request/Reply Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Identifier | Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Originate Timestamp | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Receive Timestamp | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Transmit Timestamp | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + struct icmp4_timestamp_msg{ + u16 identifier; + u16 sequence; + u32 originate_ts; + u32 receive_ts; + u32 transmit_ts; + }__attribute__((__packed__)); + typedef struct icmp4_timestamp_msg icmp4_timestamp_msg_t; + + + /* Information Request/Reply Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Identifier | Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + struct icmp4_information_msg{ + u16 identifier; + u16 sequence; + }__attribute__((__packed__)); + typedef struct icmp4_information_msg icmp4_information_msg_t; + + + /* ICMP Router Advertisement Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Num Addrs |Addr Entry Size| Lifetime | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Router Address[1] | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Preference Level[1] | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Router Address[2] | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Preference Level[2] | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | . | + | . | + | . | */ + struct icmp4_router_advert_entry{ + struct in_addr router_addr; + u32 preference_level; + }__attribute__((__packed__)); + typedef struct icmp4_router_advert_entry icmp4_router_advert_entry_t; + + struct icmp4_router_advert_msg{ + u8 num_addrs; + u8 addr_entry_size; + u16 lifetime; + icmp4_router_advert_entry_t adverts[MAX_ROUTER_ADVERT_ENTRIES]; + }__attribute__((__packed__)); + typedef struct icmp4_router_advert_msg icmp4_router_advert_msg_t; + + + /* ICMP Router Solicitation Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + struct icmp4_router_solicit_msg{ + u32 reserved; + }__attribute__((__packed__)); + typedef struct icmp4_router_solicit_msg icmp4_router_solicit_msg_t; + + + /* ICMP Security Failures Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Reserved | Pointer | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + ~ Original Internet Headers + 64 bits of Payload ~ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + struct icmp4_security_failures_msg{ + u16 reserved; + u16 pointer; + //u8 original_headers[?]; + }__attribute__((__packed__)); + typedef struct icmp4_security_failures_msg icmp4_security_failures_msg_t; + + + /* ICMP Address Mask Request/Reply Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Identifier | Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Address Mask | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + struct icmp4_address_mask_msg{ + u16 identifier; + u16 sequence; + struct in_addr address_mask; + }__attribute__((__packed__)); + typedef struct icmp4_address_mask_msg icmp4_address_mask_msg_t; + + + /* ICMP Traceroute Message + +---------------+---------------+---------------+---------------+ + | Type | Code | Checksum | + +---------------+---------------+---------------+---------------+ + | ID Number | unused | + +---------------+---------------+---------------+---------------+ + | Outbound Hop Count | Return Hop Count | + +---------------+---------------+---------------+---------------+ + | Output Link Speed | + +---------------+---------------+---------------+---------------+ + | Output Link MTU | + +---------------+---------------+---------------+---------------+ */ + struct icmp4_traceroute_msg{ + u16 id_number; + u16 unused; + u16 outbound_hop_count; + u16 return_hop_count; + u32 output_link_speed; + u32 output_link_mtu; + }__attribute__((__packed__)); + typedef struct icmp4_traceroute_msg icmp4_traceroute_msg_t; + + + /* ICMP Domain Name Request Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Identifier | Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + struct icmp4_domain_name_request_msg{ + u16 identifier; + u16 sequence; + }__attribute__((__packed__)); + typedef struct icmp4_domain_name_request_msg icmp4_domain_name_request_msg_t; + + + /* ICMP Domain Name Reply Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Identifier | Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Time-To-Live | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Names ... + +-+-+-+-+-+-+-+- */ + struct icmp4_domain_name_reply_msg{ + u16 identifier; + u16 sequence; + s16 ttl; /* Signed! */ + u8 names[ICMP_MAX_PAYLOAD_LEN-8]; + }__attribute__((__packed__)); + typedef struct icmp4_domain_name_reply_msg icmp4_domain_name_reply_msg_t; + + + /* Main data structure */ + nping_icmpv4_hdr_t h; + + /* Helper pointers */ + icmp4_dest_unreach_msg_t *h_du; + icmp4_time_exceeded_msg_t *h_te; + icmp4_parameter_problem_msg_t *h_pp; + icmp4_source_quench_msg_t *h_sq; + icmp4_redirect_msg_t *h_r; + icmp4_echo_msg_t *h_e; + icmp4_timestamp_msg_t *h_t; + icmp4_information_msg_t *h_i; + icmp4_router_advert_msg_t *h_ra; + icmp4_router_solicit_msg_t *h_rs; + icmp4_security_failures_msg_t *h_sf; + icmp4_address_mask_msg_t *h_am; + icmp4_traceroute_msg_t *h_trc; + icmp4_domain_name_request_msg_t *h_dn; + icmp4_domain_name_reply_msg_t *h_dnr; + + /* Internal counts */ + int routeradventries; + int domainnameentries; + + public: + /* PacketElement:: Mandatory methods */ + ICMPv4Header(); + ~ICMPv4Header(); + void reset(); + u8 *getBufferPointer(); + int storeRecvData(const u8 *buf, size_t len); + int protocol_id() const; + int validate(); + int print(FILE *output, int detail) const; + + /* ICMP Type */ + int setType(u8 val); + u8 getType() const; + bool validateType(); + bool validateType(u8 val); + + /* ICMP Code */ + int setCode(u8 c); + u8 getCode() const; + bool validateCode(); + bool validateCode(u8 type, u8 code); + + /* Checksum */ + int setSum(); + int setSum(u16 s); + u16 getSum() const; + + /* Unused and reserved fields */ + int setUnused(u32 val); + u32 getUnused() const; + int setReserved( u32 val ); + u32 getReserved() const; + + /* Redirect */ + int setGatewayAddress(struct in_addr ipaddr); + struct in_addr getGatewayAddress() const; + + /* Parameter problem */ + int setParameterPointer(u8 val); + u8 getParameterPointer() const; + + /* Router advertisement */ + int setNumAddresses(u8 val); + u8 getNumAddresses() const; + int setAddrEntrySize(u8 val); + u8 getAddrEntrySize() const; + int setLifetime(u16 val); + u16 getLifetime() const; + int addRouterAdvEntry(struct in_addr raddr, u32 pref); + u8 *getRouterAdvEntries(int *num) const; + int clearRouterAdvEntries(); + + /* Echo/Timestamp/Mask */ + int setIdentifier(u16 val); + u16 getIdentifier() const; + int setSequence(u16 val); + u16 getSequence() const; + + /* Timestamp only */ + int setOriginateTimestamp(u32 t); + u32 getOriginateTimestamp() const; + int setReceiveTimestamp(u32 t); + u32 getReceiveTimestamp() const; + int setTransmitTimestamp(u32 t); + u32 getTransmitTimestamp() const; + + /* Mask only */ + int setAddressMask(struct in_addr mask); + struct in_addr getAddressMask() const; + + /* Security Failures */ + int setSecurityPointer(u16 val); + u16 getSecurityPointer() const; + + /* Traceroute */ + int setIDNumber(u16 val); + u16 getIDNumber() const; + int setOutboundHopCount(u16 val); + u16 getOutboundHopCount() const; + int setReturnHopCount(u16 val); + u16 getReturnHopCount() const; + int setOutputLinkSpeed(u32 val); + u32 getOutputLinkSpeed() const; + int setOutputLinkMTU(u32 val); + u32 getOutputLinkMTU() const; + + /* Misc */ + int getICMPHeaderLengthFromType( u8 type ) const; + const char *type2string(int type, int code) const; + bool isError() const; + + +}; /* End of class ICMPv4Header */ + +#endif diff --git a/libnetutil/ICMPv6Header.cc b/libnetutil/ICMPv6Header.cc new file mode 100644 index 0000000..ee28151 --- /dev/null +++ b/libnetutil/ICMPv6Header.cc @@ -0,0 +1,1358 @@ +/*************************************************************************** + * ICMPv6Header.cc -- The ICMPv6Header Class represents an ICMP version 6 * + * packet. It contains methods to set any header field. In general, these * + * methods do error checkings and byte order conversion. * + * * + ***********************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 "ICMPv6Header.h" +#include "IPv6Header.h" +#include <assert.h> + +/******************************************************************************/ +/* CONTRUCTORS, DESTRUCTORS AND INITIALIZATION METHODS */ +/******************************************************************************/ +ICMPv6Header::ICMPv6Header() { + this->reset(); +} /* End of ICMPv6Header constructor */ + + +ICMPv6Header::~ICMPv6Header() { + +} /* End of ICMPv6Header destructor */ + + +/** Sets every attribute to its default value */ +void ICMPv6Header::reset(){ + memset(&this->h, 0, sizeof(nping_icmpv6_hdr_t)); + h_du = (dest_unreach_msg_t *)this->h.data; + h_ptb= (pkt_too_big_msg_t *)this->h.data; + h_te = (time_exceeded_msg_t *)this->h.data; + h_pp = (parameter_problem_msg_t *)this->h.data; + h_e = (echo_msg_t *)this->h.data; + h_ra = (router_advert_msg_t *)this->h.data; + h_rs = (router_solicit_msg_t *)this->h.data; + h_na = (neighbor_advert_msg_t *)this->h.data; + h_ns = (neighbor_solicit_msg_t *)this->h.data; + h_r = (redirect_msg_t *)this->h.data; + h_rr = (router_renumbering_msg_t *)this->h.data; + h_ni = (nodeinfo_msg_t *)this->h.data; + h_mld= (mld_msg_t *)this->h.data; +} /* 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 *ICMPv6Header::getBufferPointer(){ + return (u8*)(&this->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 ICMPv6Header class is able to hold a maximum of + * sizeof(nping_icmpv6_hdr_t) bytes. If the supplied buffer is longer than + * that, only the first 1508 bytes will be stored in the internal buffer. + * @warning Supplied len MUST be at least 8 bytes (min ICMPv6 header length). + * @return OP_SUCCESS on success and OP_FAILURE in case of error */ +int ICMPv6Header::storeRecvData(const u8 *buf, size_t len){ + if(buf==NULL || len<ICMPv6_MIN_HEADER_LEN){ + this->length=0; + return OP_FAILURE; + }else{ + int stored_len = MIN( sizeof(nping_icmpv6_hdr_t), len); + this->reset(); /* Re-init the object, just in case the caller had used it already */ + this->length=stored_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 ICMPv6Header::protocol_id() const { + return HEADER_TYPE_ICMPv6; +} /* 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. + * @return the length, in bytes, of the header, if its found to be valid or + * OP_FAILURE (-1) otherwise. */ +int ICMPv6Header::validate(){ + int should_have=this->getHeaderLengthFromType( this->getType() ); + if(this->length < should_have){ + return OP_FAILURE; + }else{ + /* WARNING: If we extend this class to support new ICMPv6 types with + * a variable length header (not even sure they exist), we need to + * parse the objects data and return our actual size, not this size that + * is obtained from the type. */ + return should_have; + } +} /* 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 ICMPv6Header::print(FILE *output, int detail) const { + u8 type=this->getType(); + u8 code=this->getCode(); + const char *typestr=this->type2string(type, code); + + fprintf(output, "ICMPv6[%s", typestr); + if(detail>=PRINT_DETAIL_MED) + fprintf(output, " (type=%u/code=%u)", type, code); + + switch(type) { + + case ICMPv6_UNREACH: + case ICMPv6_TIMXCEED: + if(detail>=PRINT_DETAIL_HIGH) + fprintf(output, " unused=%lu", (long unsigned int)this->getUnused()); + break; + + case ICMPv6_ROUTERSOLICIT: + if(detail>=PRINT_DETAIL_HIGH) + fprintf(output, " reserved=%lu", (long unsigned int)this->getReserved()); + break; + + case ICMPv6_PKTTOOBIG: + fprintf(output, " mtu=%lu", (long unsigned int)this->getMTU()); + break; + + case ICMPv6_PARAMPROB: + fprintf(output, " pointer=%lu", (long unsigned int)this->getPointer()); + break; + + case ICMPv6_ECHO: + case ICMPv6_ECHOREPLY: + fprintf(output, " id=%u seq=%u", this->getIdentifier(), this->getSequence()); + break; + + case ICMPv6_NODEINFOQUERY: + case ICMPv6_NODEINFORESP: + if(this->getNodeInfoFlags()!=0){ + fprintf(output, " flags="); + if(this->getNodeInfoFlags() & ICMPv6_NI_FLAG_T) + fprintf(output, "T"); + if(this->getNodeInfoFlags() & ICMPv6_NI_FLAG_A) + fprintf(output, "A"); + if(this->getNodeInfoFlags() & ICMPv6_NI_FLAG_C) + fprintf(output, "C"); + if(this->getNodeInfoFlags() & ICMPv6_NI_FLAG_L) + fprintf(output, "L"); + if(this->getNodeInfoFlags() & ICMPv6_NI_FLAG_G) + fprintf(output, "G"); + if(this->getNodeInfoFlags() & ICMPv6_NI_FLAG_S) + fprintf(output, "S"); + } + if(detail>=PRINT_DETAIL_HIGH){ + #ifdef WIN32 + fprintf(output, " nonce=%I64u", (long long unsigned int)this->getNonce()); + #else + fprintf(output, " nonce=%llu", (long long unsigned int)this->getNonce()); + #endif + } + break; + + default: + /* Print nothing */ + break; + } + + if(detail>=PRINT_DETAIL_HIGH) + fprintf(output, " csum=0x%04X", ntohs(this->getSum())); + fprintf(output, "]"); + if(this->next!=NULL){ + print_separator(output, detail); + next->print(output, detail); + } + return OP_SUCCESS; +} /* End of print() */ + + +/******************************************************************************/ +/* PROTOCOL-SPECIFIC METHODS */ +/******************************************************************************/ + +/******************************************************************************/ +/* ICMPv6 COMMON HEADER */ +/******************************************************************************/ + +/** Set ICMPv6 type field */ +int ICMPv6Header::setType(u8 val){ + this->h.type = val; + this->length = getHeaderLengthFromType(val); + return OP_SUCCESS; +} /* End of setType() */ + + +/** Returns ICMPv6 type field */ +u8 ICMPv6Header::getType() const { + return this->h.type; +} /* End of getType() */ + + +/* Returns true if the supplied ICMPv6 type is supported by this class */ +bool ICMPv6Header::validateType(u8 val){ + switch( val ){ + case ICMPv6_UNREACH: + case ICMPv6_PKTTOOBIG: + case ICMPv6_TIMXCEED: + case ICMPv6_PARAMPROB: + case ICMPv6_ECHO: + case ICMPv6_ECHOREPLY: + case ICMPv6_ROUTERSOLICIT: + case ICMPv6_ROUTERADVERT: + case ICMPv6_NGHBRSOLICIT: + case ICMPv6_NGHBRADVERT: + case ICMPv6_REDIRECT: + case ICMPv6_RTRRENUM: + return true; + break; + + default: + return false; + break; + } + return false; +} /* End of validateType() */ + + +bool ICMPv6Header::validateType(){ + return validateType(this->h.type); +} /* End of validateType() */ + + +/** Set ICMPv6 code field */ +int ICMPv6Header::setCode(u8 val){ + this->h.code = val; + return OP_SUCCESS; +} /* End of setCode() */ + + +/** Returns ICMPv6 code field */ +u8 ICMPv6Header::getCode() const { + return this->h.code; +} /* End of getCode() */ + + +/** Given an ICMP Type and a code, determines whether the code corresponds to + * a RFC compliant code (eg: code 0x03 for "port unreachable" in ICMP + * Unreachable messages) or just some other bogus code. */ +bool ICMPv6Header::validateCode(u8 type, u8 code){ +// switch (type){ +// +// case ICMPv6_UNREACH: +// return (code==0); +// break; +// +// case ICMPv6_PKTTOOBIG: +// switch( code ){ +// case XXXXXXXXXXXX: +// case YYYYYYYYYYYY: +// case ZZZZZZZZZZZZ: +// return true; +// break; +// } +// break; +// +// case ICMPv6_TIMXCEED: +// +// break; +// +// case ICMPv6_PARAMPROB: +// +// break; +// +// case ICMPv6_ECHO: +// +// break; +// +// case ICMPv6_ECHOREPLY: +// +// break; +// +// case ICMPv6_ROUTERSOLICIT: +// case ICMPv6_ROUTERADVERT: +// case ICMPv6_NGHBRSOLICIT: +// case ICMPv6_NGHBRADVERT: +// case ICMPv6_REDIRECT: +// break; +// +// default: +// return false; +// break; +// } + return false; +} /* End of validateCode() */ + + +/** Computes the ICMP header checksum and sets the checksum field to the right + * value. + * @warning This method requires the ICMPv6Object to be linked to an IPv6Header + * object, so make sure setNextElement() has been called like this: + * + * IPv6Header ip6; + * ICMPv6Header icmp6; + * [...] # Set header fields + * ip6.setNextElement(&icmp6); + * icmp6.setSum(); + * + * Note that there can be a number of extension headers between the ICMPv6 + * header and the IPv6 one, but all of them need to be linked in order for this + * method to traverse the list of headers and find the IPv6 source and + * destination address, required to compute the checksum. So things like the + * following are OK: + * + * IPv6Header ip6; + * HopByHopHeader hop; + * RoutingHeader rte; + * FragmentHeader frg; + * ICMPv6Header icmp6; + * [...] # Set whatever header fields you need + * ip6.setNextElement(&hop); + * hop.setNextElement(&rte); + * rte.setNextElement(&frg); + * frg.setNextElement(&icmp6); + * icmp6.setSum(); # setSum() will be able to reach the IPv6Header. + * + */ +int ICMPv6Header::setSum(){ + PacketElement *hdr; + hdr=this->getPrevElement(); + /* Traverse the list of headers backwards until we find the IPv6 header */ + while(hdr!=NULL){ + if (hdr->protocol_id()==HEADER_TYPE_IPv6){ + IPv6Header *v6hdr=(IPv6Header *)hdr; + struct in6_addr i6src, i6dst; + this->h.checksum=0; + memcpy(i6src.s6_addr, v6hdr->getSourceAddress(), 16); + memcpy(i6dst.s6_addr, v6hdr->getDestinationAddress(), 16); + u8 *buff=(u8 *)safe_malloc(this->getLen()); + this->dumpToBinaryBuffer(buff, this->getLen()); + this->h.checksum=ipv6_pseudoheader_cksum(&i6src, &i6dst, this->protocol_id(), this->getLen(), buff); + free(buff); + return OP_SUCCESS; + }else{ + hdr=hdr->getPrevElement(); + } + } + return OP_FAILURE; +} /* End of setSum() */ + + +/** @warning Sum is set to supplied value with NO byte ordering conversion + * performed. + * @warning If sum is supplied this way, no error checks are made. Caller is + * responsible for the correctness of the value. */ +int ICMPv6Header::setSum(u16 s){ + this->h.checksum=s; + return OP_SUCCESS; +} /* End of setSum() */ + + +/** Returns the value of the checksum field. + * @warning The returned value is in NETWORK byte order, no conversion is + * performed */ +u16 ICMPv6Header::getSum() const{ + return this->h.checksum; +} /* End of getSum() */ + + +/** @warning Supplied value MUST be in host byte order because it will get + * converted by this method using htonl() */ +int ICMPv6Header::setReserved(u32 val){ + u32 aux32=0; + u8 *auxpnt=(u8 *)&aux32; + + switch(this->h.type){ + + case ICMPv6_UNREACH: + this->h_du->unused=htonl(val); + break; + + case ICMPv6_TIMXCEED: + this->h_te->unused=htonl(val); + break; + + case ICMPv6_ROUTERSOLICIT: + this->h_rs->reserved=htonl(val); + break; + + case ICMPv6_NGHBRSOLICIT: + this->h_ns->reserved=htonl(val); + break; + + case ICMPv6_REDIRECT: + this->h_r->reserved=htonl(val); + break; + + + case ICMPv6_NGHBRADVERT: + /* The reserved field in Neighbor Advertisement messages is only + * 24-bits long so we convert the supplied value to big endian and + * use only the 24 least significant bits. */ + aux32=htonl(val); + this->h_na->reserved[0]=auxpnt[1]; + this->h_na->reserved[1]=auxpnt[2]; + this->h_na->reserved[2]=auxpnt[3]; + break; + + case ICMPv6_RTRRENUM: + this->h_rr->reserved=htonl(val); + break; + + /* Types that don't have a reserved field */ + case ICMPv6_ROUTERADVERT: + case ICMPv6_ECHO: + case ICMPv6_ECHOREPLY: + case ICMPv6_PARAMPROB: + case ICMPv6_PKTTOOBIG: + default: + return OP_FAILURE; + break; + } + return OP_SUCCESS; +} /* End of setReserved() */ + + +/** @warning Returned value is in host byte order */ +u32 ICMPv6Header::getReserved() const { + u32 aux32=0; + u8 *auxpnt=(u8 *)&aux32; + + switch(this->h.type){ + + case ICMPv6_UNREACH: + return ntohl(this->h_du->unused); + break; + + case ICMPv6_TIMXCEED: + return ntohl(this->h_te->unused); + break; + + case ICMPv6_ROUTERSOLICIT: + return ntohl(this->h_rs->reserved); + break; + + case ICMPv6_NGHBRSOLICIT: + return ntohl(this->h_ns->reserved); + break; + + case ICMPv6_REDIRECT: + return ntohl(this->h_r->reserved); + break; + + case ICMPv6_NGHBRADVERT: + /* The reserved field in Neighbor Advertisement messages is only + * 24-bits long so we extract the stored value and convert it to host + * byte order. */ + auxpnt[0]=0; + auxpnt[1]=this->h_na->reserved[0]; + auxpnt[2]=this->h_na->reserved[1]; + auxpnt[3]=this->h_na->reserved[2]; + return ntohl(aux32); + break; + + case ICMPv6_RTRRENUM: + return ntohl(this->h_rr->reserved); + break; + + /* Types that don't have a reserved field */ + case ICMPv6_ROUTERADVERT: + case ICMPv6_ECHO: + case ICMPv6_ECHOREPLY: + case ICMPv6_PARAMPROB: + case ICMPv6_PKTTOOBIG: + default: + return 0; + break; + } +} /* End of setReserved() */ + +int ICMPv6Header::setUnused(u32 val){ + return this->setReserved(val); +} /* End of setUnused() */ + + +u32 ICMPv6Header::getUnused() const { + return this->getReserved(); +} /* End of getUnused() */ + + +int ICMPv6Header::setFlags(u8 val){ + switch(this->h.type){ + + case ICMPv6_ROUTERADVERT: + this->h_ra->autoconfig_flags=val; + break; + + case ICMPv6_NGHBRADVERT: + this->h_na->flags=val; + break; + + case ICMPv6_RTRRENUM: + this->h_rr->flags=val; + break; + + case ICMPv6_NODEINFOQUERY: + case ICMPv6_NODEINFORESP: + netutil_fatal("setFlags() cannot be used in NI, use setNodeInfoFlags() instead\n"); + break; + + /* Types that don't have a flags field */ + case ICMPv6_TIMXCEED: + case ICMPv6_UNREACH: + case ICMPv6_ROUTERSOLICIT: + case ICMPv6_NGHBRSOLICIT: + case ICMPv6_REDIRECT: + case ICMPv6_ECHO: + case ICMPv6_ECHOREPLY: + case ICMPv6_PARAMPROB: + case ICMPv6_PKTTOOBIG: + default: + return OP_FAILURE; + break; + } + return OP_SUCCESS; +} /* End of setFlags() */ + + +u8 ICMPv6Header::getFlags() const { + switch(this->h.type){ + + case ICMPv6_ROUTERADVERT: + return this->h_ra->autoconfig_flags; + break; + + case ICMPv6_NGHBRADVERT: + return this->h_na->flags; + break; + + case ICMPv6_RTRRENUM: + return this->h_rr->flags; + break; + + case ICMPv6_NODEINFOQUERY: + case ICMPv6_NODEINFORESP: + netutil_fatal("getFlags() cannot be used in NI, use getNodeInfoFlags() instead\n"); + return 0; + break; + + /* Types that don't have a flags field */ + case ICMPv6_TIMXCEED: + case ICMPv6_UNREACH: + case ICMPv6_ROUTERSOLICIT: + case ICMPv6_NGHBRSOLICIT: + case ICMPv6_REDIRECT: + case ICMPv6_ECHO: + case ICMPv6_ECHOREPLY: + case ICMPv6_PARAMPROB: + case ICMPv6_PKTTOOBIG: + default: + return 0; + break; + } +} /* End of getFlags() */ + +/******************************************************************************/ +/* ICMPv6 DESTINATION UNREACHABLE */ +/******************************************************************************/ + +/******************************************************************************/ +/* ICMPv6 PACKET TOO BIG */ +/******************************************************************************/ +int ICMPv6Header::setMTU(u32 mtu){ + this->h_ptb->mtu=htonl(mtu); + return OP_SUCCESS; +} /* End of setMTU() */ + +u32 ICMPv6Header::getMTU() const { + return ntohl(this->h_ptb->mtu); +} /* End of getMTU() */ + +/******************************************************************************/ +/* ICMPv6 TIME EXCEEDED */ +/******************************************************************************/ + +/******************************************************************************/ +/* ICMPv6 PARAMETER PROBLEM */ +/******************************************************************************/ +int ICMPv6Header::setPointer(u32 pnt){ + this->h_pp->pointer=htonl(pnt); + return OP_SUCCESS; +} /* End of setPointer() */ + + +u32 ICMPv6Header::getPointer() const { + return ntohl(this->h_pp->pointer); +} /* End of getPointer() */ + +/******************************************************************************/ +/* ICMPv6 ECHO */ +/******************************************************************************/ +int ICMPv6Header::setIdentifier(u16 val){ + this->h_e->id=htons(val); + return OP_SUCCESS; +} /* End of setIdentifier() */ + + +u16 ICMPv6Header::getIdentifier() const{ + return ntohs(this->h_e->id); +} /* End of getIdentifier() */ + + +int ICMPv6Header::setSequence(u16 val){ + switch(this->h.type){ + case ICMPv6_RTRRENUM: + this->h_rr->seq=htonl( ((u32)val) ); + break; + + case ICMPv6_ECHO: + case ICMPv6_ECHOREPLY: + this->h_e->seq=htons(val); + break; + + default: + return OP_FAILURE; + break; + } + return OP_SUCCESS; +} /* End of setSequence() */ + + +int ICMPv6Header::setSequence(u32 val){ + switch(this->h.type){ + case ICMPv6_RTRRENUM: + this->h_rr->seq=htonl(val); + break; + + case ICMPv6_ECHO: + case ICMPv6_ECHOREPLY: + this->h_e->seq=htons( ((u16)val) ); + break; + + default: + return OP_FAILURE; + break; + } + return OP_SUCCESS; +} /* End of setSequence() */ + + +u32 ICMPv6Header::getSequence() const{ + switch(this->h.type){ + case ICMPv6_RTRRENUM: + return ntohl(this->h_rr->seq); + break; + + case ICMPv6_ECHO: + case ICMPv6_ECHOREPLY: + return (u32)ntohs(this->h_e->seq); + break; + } + return 0; +} /* End of getSequence() */ + + +/******************************************************************************/ +/* ICMPv6 ROUTER ADVERTISEMENT */ +/******************************************************************************/ +int ICMPv6Header::setCurrentHopLimit(u8 val){ + this->h_ra->current_hop_limit=val; + return OP_SUCCESS; +} /* End of setCurrentHopLimit() */ + +u8 ICMPv6Header::getCurrentHopLimit() const { + return this->h_ra->current_hop_limit; +} /* End of getCurrentHopLimit() */ + +int ICMPv6Header::setRouterLifetime(u16 val){ + this->h_ra->router_lifetime=val; + return OP_SUCCESS; +} /* End of setRouterLifetime() */ + +u16 ICMPv6Header::getRouterLifetime() const { + return this->h_ra->router_lifetime; +} /* End of getRouterLifetime() */ + +int ICMPv6Header::setReachableTime(u32 val){ + this->h_ra->reachable_time=val; + return OP_SUCCESS; +} /* End of setReachableTime() */ + +u32 ICMPv6Header::getReachableTime() const { + return this->h_ra->reachable_time; +} /* End of getReachableTime() */ + +int ICMPv6Header::setRetransmissionTimer(u32 val){ + this->h_ra->retransmission_timer=val; + return OP_SUCCESS; +} /* End of setRetransmissionTimer() */ + +u32 ICMPv6Header::getRetransmissionTimer() const { + return this->h_ra->retransmission_timer; +} /* End of getRetransmissionTimer() */ + +/******************************************************************************/ +/* ICMPv6 ROUTER SOLICITATION */ +/******************************************************************************/ + +/******************************************************************************/ +/* ICMPv6 NEIGHBOR ADVERTISEMENT */ +/******************************************************************************/ + +int ICMPv6Header::setTargetAddress(struct in6_addr addr){ + switch(this->h.type){ + case ICMPv6_NGHBRADVERT: + memcpy(this->h_na->target_address, addr.s6_addr, 16); + break; + + case ICMPv6_NGHBRSOLICIT: + memcpy(this->h_ns->target_address, addr.s6_addr, 16); + break; + + case ICMPv6_REDIRECT: + memcpy(this->h_r->target_address, addr.s6_addr, 16); + break; + + default: + return OP_FAILURE; + break; + } + return OP_SUCCESS; +} /* End of setTargetAddress() */ + + +struct in6_addr ICMPv6Header::getTargetAddress() const { + struct in6_addr addr; + memset(&addr, 0, sizeof(struct in6_addr)); + + switch(this->h.type){ + case ICMPv6_NGHBRADVERT: + memcpy(addr.s6_addr, this->h_na->target_address, 16); + break; + + case ICMPv6_NGHBRSOLICIT: + memcpy(addr.s6_addr, this->h_ns->target_address, 16); + break; + + case ICMPv6_REDIRECT: + memcpy(addr.s6_addr, this->h_r->target_address, 16); + break; + } + return addr; +} /* End of setTargetAddress() */ + + +int ICMPv6Header::setDestinationAddress(struct in6_addr addr){ + switch(this->h.type){ + case ICMPv6_REDIRECT: + memcpy(this->h_r->destination_address, addr.s6_addr, 16); + break; + + default: + return OP_FAILURE; + break; + } + return OP_SUCCESS; +} /* End of setDestinationAddress() */ + + +struct in6_addr ICMPv6Header::getDestinationAddress() const { + struct in6_addr addr; + memset(&addr, 0, sizeof(struct in6_addr)); + + switch(this->h.type){ + case ICMPv6_REDIRECT: + memcpy(addr.s6_addr, this->h_r->destination_address, 16); + break; + } + return addr; +} /* End of setTargetAddress() */ + + +/******************************************************************************/ +/* ICMPv6 NEIGHBOR SOLICITATION */ +/******************************************************************************/ + +/******************************************************************************/ +/* ICMPv6 REDIRECT */ +/******************************************************************************/ + +/******************************************************************************/ +/* ICMPv6 ROUTER RENUMBERING */ +/******************************************************************************/ +int ICMPv6Header::setSegmentNumber(u8 val){ + this->h_rr->segment_number=val; + return OP_SUCCESS; +} /* End of setSegmentNumber() */ + +u8 ICMPv6Header::getSegmentNumber() const { + return this->h_rr->segment_number; +} /* End of getSegmentNumber() */ + +int ICMPv6Header::setMaxDelay(u16 val){ + switch(this->h.type){ + case ICMPv6_RTRRENUM: + this->h_rr->max_delay=htons(val); + return OP_SUCCESS; + break; + + case ICMPv6_GRPMEMBQUERY: + case ICMPv6_GRPMEMBREP: + case ICMPv6_GRPMEMBRED: + this->h_mld->max_response_delay=htons(val); + return OP_SUCCESS; + break; + + default: + return OP_FAILURE; + break; + } +} /* End of setMaxDelay() */ + + +u16 ICMPv6Header::getMaxDelay() const { + switch(this->h.type){ + case ICMPv6_RTRRENUM: + return ntohs(this->h_rr->max_delay); + break; + + case ICMPv6_GRPMEMBQUERY: + case ICMPv6_GRPMEMBREP: + case ICMPv6_GRPMEMBRED: + return ntohs(this->h_mld->max_response_delay); + break; + + default: + return 0; + break; + } +} /* End of getMaxDelay() */ + + + +/******************************************************************************/ +/* ICMPv6 NODE INFORMATION QUERIES */ +/******************************************************************************/ +/** Set NI Qtype */ +int ICMPv6Header::setQtype(u16 val){ + this->h_ni->qtype = htons(val); + return OP_SUCCESS; +} /* End of setQtype() */ + + +/** Returns NI Qtype */ +u16 ICMPv6Header::getQtype() const { + return ntohs(this->h_ni->qtype); +} /* End of getQtype() */ + + +/** Set NI Flags */ +int ICMPv6Header::setNodeInfoFlags(u16 val){ + this->h_ni->flags = htons(val); + return OP_SUCCESS; +} /* End of setNodeInfoFlags() */ + + +/** Returns NI Flags */ +u16 ICMPv6Header::getNodeInfoFlags() const { + return ntohs(this->h_ni->flags); +} /* End of getNodeInfoFlags() */ + + +/* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | unused |G|S|L|C|A|T| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + +/* Set NI Flag G */ +int ICMPv6Header::setG(bool flag_value){ + u16 current_flags = this->getNodeInfoFlags(); + if(flag_value) + current_flags = current_flags | 0x0020; + else + current_flags = current_flags & ~0x0020; + this->setNodeInfoFlags(current_flags); + return OP_SUCCESS; +} /* End of setG() */ + + +/* Get NI Flag G */ +bool ICMPv6Header::getG() const { + return this->getNodeInfoFlags() & 0x0020; +} /* End of getG() */ + + +/* Set NI Flag S */ +int ICMPv6Header::setS(bool flag_value){ + u16 current_flags = this->getNodeInfoFlags(); + if(flag_value) + current_flags = current_flags | 0x0010; + else + current_flags = current_flags & ~0x0010; + this->setNodeInfoFlags(current_flags); + return OP_SUCCESS; +} /* End of setS() */ + + +/* Get NI Flag S */ +bool ICMPv6Header::getS() const { + return this->getNodeInfoFlags() & 0x0010; +} /* End of getS() */ + + +/* Set NI Flag L */ +int ICMPv6Header::setL(bool flag_value){ + u16 current_flags = this->getNodeInfoFlags(); + if(flag_value) + current_flags = current_flags | 0x0008; + else + current_flags = current_flags & ~0x0008; + this->setNodeInfoFlags(current_flags); + return OP_SUCCESS; +} /* End of setL() */ + + +/* Get NI Flag L */ +bool ICMPv6Header::getL() const { + return this->getNodeInfoFlags() & 0x0008; +} /* End of getL() */ + + +/* Set NI Flag C */ +int ICMPv6Header::setC(bool flag_value){ + u16 current_flags = this->getNodeInfoFlags(); + if(flag_value) + current_flags = current_flags | 0x0004; + else + current_flags = current_flags & ~0x0004; + this->setNodeInfoFlags(current_flags); + return OP_SUCCESS; +} /* End of setC() */ + + +/* Get NI Flag C */ +bool ICMPv6Header::getC() const { + return this->getNodeInfoFlags() & 0x0004; +} /* End of getC() */ + + +/* Set NI Flag A */ +int ICMPv6Header::setA(bool flag_value){ + u16 current_flags = this->getNodeInfoFlags(); + if(flag_value) + current_flags = current_flags | 0x0002; + else + current_flags = current_flags & ~0x0002; + this->setNodeInfoFlags(current_flags); + return OP_SUCCESS; +} /* End of setA() */ + + +/* Get NI Flag A */ +bool ICMPv6Header::getA() const { + return this->getNodeInfoFlags() & 0x0002; +} /* End of getA() */ + + +/* Set NI Flag T */ +int ICMPv6Header::setT(bool flag_value){ + u16 current_flags = this->getNodeInfoFlags(); + if(flag_value) + current_flags = current_flags | 0x0001; + else + current_flags = current_flags & ~0x0001; + this->setNodeInfoFlags(current_flags); + return OP_SUCCESS; +} /* End of setT() */ + + +/* Get NI Flag T */ +bool ICMPv6Header::getT() const { + return this->getNodeInfoFlags() & 0x0001; +} /* End of getT() */ + + +/* Set the Nonce field. */ +int ICMPv6Header::setNonce(u64 nonce_value){ + this->h_ni->nonce=nonce_value; + return OP_SUCCESS; +} /* End of setNonce() */ + + +/* Set the Nonce field. + * @warning: Supplied buffer must contain 8 bytes. */ +int ICMPv6Header::setNonce(const u8 *nonce){ + if(nonce==NULL) + return OP_FAILURE; + memcpy(&(this->h_ni->nonce), nonce, NI_NONCE_LEN); + return OP_SUCCESS; +} /* End of setNonce() */ + + +/* Returns a pointer to the nonce buffer. + * @warning: The returned pointer is guaranteed to point to an 8-byte buffer. + * However, what comes after the 8th byte is unspecified. */ +u64 ICMPv6Header::getNonce() const { + return this->h_ni->nonce; +} /* End of getNonce() */ + + +/******************************************************************************/ +/* MULTICAST LISTENER DISCOVERY */ +/******************************************************************************/ + +int ICMPv6Header::setMulticastAddress(struct in6_addr addr){ + switch(this->h.type){ + case ICMPv6_GRPMEMBQUERY: + case ICMPv6_GRPMEMBREP: + case ICMPv6_GRPMEMBRED: + memcpy(this->h_mld->mcast_address, addr.s6_addr, 16); + break; + + default: + return OP_FAILURE; + break; + } + + return OP_SUCCESS; +} /* End of setMulticastAddress() */ + + +struct in6_addr ICMPv6Header::getMulticastAddress() const { + struct in6_addr addr; + memset(&addr, 0, sizeof(struct in6_addr)); + + switch(this->h.type){ + case ICMPv6_GRPMEMBQUERY: + case ICMPv6_GRPMEMBREP: + case ICMPv6_GRPMEMBRED: + memcpy(addr.s6_addr, this->h_mld->mcast_address, 16); + break; + } + return addr; +} /* End of setMulticastAddress() */ + + +/******************************************************************************/ +/* MISCELLANEOUS STUFF */ +/******************************************************************************/ + +/** Returns the standard ICMPv6 header length for the supplied ICMP message type. + * @warning Return value corresponds strictly to the ICMP header, this is, + * the minimum length of the ICMP header, variable length payload is never + * included. For example, an ICMPv6 Redirect has a fixed header of 40 + * bytes but then the packet may contain ICMPv6 options. We only return 40 + * because we don't know in advance the total number of bytes for the message. + * Same applies to the rest of types. */ +int ICMPv6Header::getHeaderLengthFromType(u8 type) const { + + switch( type ){ + case ICMPv6_UNREACH: + return ICMPv6_UNREACH_LEN; + break; + case ICMPv6_PKTTOOBIG: + return ICMPv6_PKTTOOBIG_LEN; + break; + + case ICMPv6_TIMXCEED: + return ICMPv6_TIMXCEED_LEN; + break; + + case ICMPv6_PARAMPROB: + return ICMPv6_PARAMPROB_LEN; + break; + + case ICMPv6_ECHO: + return ICMPv6_ECHO_LEN; + break; + + case ICMPv6_ECHOREPLY: + return ICMPv6_ECHOREPLY_LEN; + break; + + case ICMPv6_ROUTERSOLICIT: + return ICMPv6_ROUTERSOLICIT_LEN; + break; + + case ICMPv6_ROUTERADVERT: + return ICMPv6_ROUTERADVERT_LEN; + break; + + case ICMPv6_NGHBRSOLICIT: + return ICMPv6_NGHBRSOLICIT_LEN; + break; + + case ICMPv6_NGHBRADVERT: + return ICMPv6_NGHBRADVERT_LEN; + break; + + case ICMPv6_REDIRECT: + return ICMPv6_REDIRECT_LEN; + break; + + case ICMPv6_RTRRENUM: + return ICMPv6_RTRRENUM_LEN; + break; + + case ICMPv6_NODEINFOQUERY: + case ICMPv6_NODEINFORESP: + return ICMPv6_NODEINFO_LEN; + break; + + case ICMPv6_GRPMEMBQUERY: + case ICMPv6_GRPMEMBREP: + case ICMPv6_GRPMEMBRED: + return ICMPv6_MLD_LEN; + break; + + /* Packets with non RFC-Compliant types will be represented as an 8-byte + * ICMPv6 header, just like the types that don't include additional info */ + default: + return ICMPv6_MIN_HEADER_LEN; + break; + } +} /* End of getHeaderLengthFromType() */ + + +/* Returns true if the packet is an ICMPv6 error message. */ +bool ICMPv6Header::isError() const { + switch( this->getType() ){ + case ICMPv6_UNREACH: + case ICMPv6_PKTTOOBIG: + case ICMPv6_TIMXCEED: + case ICMPv6_PARAMPROB: + return true; + break; + + default: + return false; + break; + } +} /* End of isError() */ + + +const char *ICMPv6Header::type2string(int type, int code) const { + switch(type) { + + case ICMPv6_UNREACH: + switch(code) { + case ICMPv6_UNREACH_NO_ROUTE: return "Network unreachable"; break; + case ICMPv6_UNREACH_PROHIBITED: return "Comm prohibited"; break; + case ICMPv6_UNREACH_BEYOND_SCOPE: return "Beyond scope"; break; + case ICMPv6_UNREACH_ADDR_UNREACH: return "Address unreachable"; break; + case ICMPv6_UNREACH_PORT_UNREACH: return "Port unreachable"; break; + case ICMPv6_UNREACH_SRC_ADDR_FAILED: return "Source address failed"; break; + case ICMPv6_UNREACH_REJECT_ROUTE: return "Reject route"; break; + default: return "Destination unreachable (unknown code)"; break; + } + break; + + case ICMPv6_PKTTOOBIG: + return "Packet too big"; + break; + + case ICMPv6_TIMXCEED: + switch(code){ + case ICMPv6_TIMXCEED_HOP_EXCEEDED: return "HopLimit=0 in transit"; break; + case ICMPv6_TIMXCEED_REASS_EXCEEDED: return "Reassembly time exceeded"; break; + default: return "Time exceeded (unknown code)"; break; + } + break; + + case ICMPv6_PARAMPROB: + switch(code){ + case ICMPv6_PARAMPROB_FIELD: return "Parameter problem (bad field)"; break; + case ICMPv6_PARAMPROB_NEXT_HDR: return "Parameter problem (next header unknown)"; break; + case ICMPv6_PARAMPROB_OPTION: return "Parameter problem (bad option)"; break; + default: return "Parameter problem (unknown code)"; break; + } + break; + + case ICMPv6_ECHO: + return "Echo request"; + break; + case ICMPv6_ECHOREPLY: + return "Echo reply"; + break; + case ICMPv6_GRPMEMBQUERY: + return "Group membership query"; + break; + case ICMPv6_GRPMEMBREP: + return "Group membership report"; + break; + case ICMPv6_GRPMEMBRED: + return "Group membership reduction"; + break; + case ICMPv6_ROUTERSOLICIT: + return "Router sol"; + break; + case ICMPv6_ROUTERADVERT: + return "Router advert"; + break; + case ICMPv6_NGHBRSOLICIT: + return "Neighbor sol"; + break; + case ICMPv6_NGHBRADVERT: + return "Neighbor advert"; + break; + case ICMPv6_REDIRECT: + return "Redirect"; + break; + case ICMPv6_RTRRENUM: + switch(code){ + case ICMPv6_RTRRENUM_COMMAND: return "Renumbering command"; break; + case ICMPv6_RTRRENUM_RESULT: return "Renumbering result"; break; + case ICMPv6_RTRRENUM_SEQ_RESET: return "Renumbering reset"; break; + default: return "Router Renumbering (unknown code)"; break; + } + break; + case ICMPv6_NODEINFOQUERY: + switch(code){ + case ICMPv6_NODEINFOQUERY_IPv6ADDR: return "Node info query (IPv6 addr)"; break; + case ICMPv6_NODEINFOQUERY_NAME: return "Node info query (name)"; break; + case ICMPv6_NODEINFOQUERY_IPv4ADDR: return "Node info query (IPv4 addr)"; break; + default: return "Node info query (unknown code)"; break; + } + break; + + case ICMPv6_NODEINFORESP: + switch(code){ + case ICMPv6_NODEINFORESP_SUCCESS: return "Node info reply (success)"; break; + case ICMPv6_NODEINFORESP_REFUSED: return "Node info reply (refused)"; break; + case ICMPv6_NODEINFORESP_UNKNOWN: return "Node info reply (qtype unknown)"; break; + default: return "Node info reply (unknown code)"; break; + } + break; + + case ICMPv6_INVNGHBRSOLICIT: + return "Inverse neighbor sol"; + break; + + case ICMPv6_INVNGHBRADVERT: + return "Inverse neighbor advert"; + break; + + case ICMPv6_MLDV2: + return "MLDv2 report"; + break; + + case ICMPv6_AGENTDISCOVREQ: + return "Home agent request"; + break; + + case ICMPv6_AGENTDISCOVREPLY: + return "Home agent reply"; + break; + + case ICMPv6_MOBPREFIXSOLICIT: + return "Prefix sol"; + break; + + case ICMPv6_MOBPREFIXADVERT: + return "Prefix advert"; + break; + + case ICMPv6_CERTPATHSOLICIT: + return "Cert path sol"; + break; + + case ICMPv6_CERTPATHADVERT: + return "Cert path advert"; + break; + + case ICMPv6_EXPMOBILITY: + return "Experimental mobility"; + break; + + case ICMPv6_MRDADVERT: + return "Multicast router advert"; + break; + + case ICMPv6_MRDSOLICIT: + return "Multicast router sol"; + break; + + case ICMPv6_MRDTERMINATE: + return "Multicast router term"; + break; + + case ICMPv6_FMIPV6: + return "FMIPv6"; + break; + + default: + return "Unknown ICMPv6 type"; + break; + } /* End of ICMP Type switch */ + return "Unknown ICMPv6 type"; +} /* End of type2string() */ + + + diff --git a/libnetutil/ICMPv6Header.h b/libnetutil/ICMPv6Header.h new file mode 100644 index 0000000..f496caa --- /dev/null +++ b/libnetutil/ICMPv6Header.h @@ -0,0 +1,669 @@ +/*************************************************************************** + * ICMPv6Header.h -- The ICMPv6Header Class represents an ICMP version 6 * + * packet. It contains methods to set any header field. In general, these * + * methods do error checkings and byte order conversion. * + * * + ***********************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. */ + +#ifndef ICMPv6HEADER_H +#define ICMPv6HEADER_H 1 + +#include "ICMPHeader.h" + +/******************************************************************************/ +/* IMPORTANT INFORMATION ON HOW TO USE THIS CLASS. */ +/******************************************************************************/ +/* This class represents an ICMPv6 messages. ICMPv6 messages may be of + * different types. Each type has its own header and possibly a variable + * length data field. Information messages have an "invoking packet" field + * which is the IP packet that triggered the emission of the ICMPv6 message. + * Other messages may contain a "data" field, like echo requests an replies. + * Some others may contain ICMPv6 Options. + * + * So the thing is, that this class only represents fixed-length ICMPv6 + * headers and does NOT offer storage for ANY variable-length field. This + * fields may be added to the ICMPv6 header using instances of the RawData + * class the ICMPv6Option class or even the IPv6Header class (in those cases + * where a whole packet is appendend to the ICMPv6 message). + * + * So, how does this work? Let's look at some examples. + * + * 1. Imagine we need to build an ICMP echo request message that includes some + * arbitrary data to be echoed. We could do the following: + * + * u8 final_packet[1024]; <-- Buffer to store the resulting packet + * u32 final_packet_len=0; <-- Length of the resulting packet + * ICMPv6Header header; <-- The ICMPv6 fixed-length part + * RawData data; <-- The data to append to the echo message + * + * header.setType(ICMPv6_ECHO); <-- Set ICMPv6 type to "Echo request" + * data.store("1234567890"); <-- Store data we need to send. + * header.setNextElement(&data); <-- Tell ICMPv6Header what's after it + * header.setSum(); <-- Compute the checksum + * + * final_packet_len=header.dumpToBinaryBuffer(fina_packet, 1024); + * send_packet(final_packet, final_packet_len) + * + * 2. If we are sending a parameter problem message and we need to include the + * invoking datagram, we can call setNextElement() passing an IPv6Header + * pointer. + * + * u8 final_packet[1024]; <-- Buffer to store the resulting packet + * u32 final_packet_len=0; <-- Length of the resulting packet + * ICMPv6Header header; <-- The ICMPv6 fixed-length part + * IPv6Header ipv6; <-- The IPv6 packet that triggered ICMPv6 + * + * header.setType(ICMPv6_PARAMPROB); <-- Set ICMPv6 type to "Param Problem" + * header.setNextElement(&ipv6); <-- Tell ICMPv6Header what's after it + * header.setSum(); <-- Compute the checksum + * + * Note that here we don't show how the ipv6 object is set. + * + * 3. If we are sending a router solicitation message, we'll call + * setNextElement() passing an IPv6Options Pointer. + * + * u8 final_packet[1024]; <-- Buffer to store the resulting packet + * u32 final_packet_len=0; <-- Length of the resulting packet + * ICMPv6Header header; <-- The ICMPv6 fixed-length part + * IPv6Options opts1; <-- IPv6 options + * IPv6Options opts2; <-- IPv6 options + * IPv6Options opts3; <-- IPv6 options + * + * header.setType(ICMPv6_ROUTERSOLICIT); <-- Set ICMPv6 type + * + * opts1.setXXXX(); <-- Set up the options + * . + * . + * . + * opts3.setYYYY(); + * + * opts2.setNextElement(&opts3); <-- Link the options + * opts1.setNextElement(&opts2); + * header.setNextElement(&opts1); + * header.setNextElement(&ipv6); <-- Link the first option to the ICMPv6 + * header.setSum(); <-- Compute the checksum + * + * And so on... + * + */ + + +/* Packet header diagrams included in this file have been taken from the + * following IETF RFC documents: RFC 4443, RFC 2461, RFC 2894 */ + +/* ICMP types and codes. + * The following types and codes have been defined by IANA. A complete list + * may be found at http://www.iana.org/assignments/icmpv6-parameters + * + * Definitions on the first level of indentation are ICMPv6 Types. + * Definitions on the second level of indentation (values enclosed in + * parenthesis) are ICMPv6 Codes */ +#define ICMPv6_UNREACH 1 /* Destination unreachable [RFC 2463, 4443] */ +#define ICMPv6_UNREACH_NO_ROUTE (0) /* --> No route to destination */ +#define ICMPv6_UNREACH_PROHIBITED (1) /* --> Communication administratively prohibited */ +#define ICMPv6_UNREACH_BEYOND_SCOPE (2) /* --> Beyond scope of source address [RFC4443] */ +#define ICMPv6_UNREACH_ADDR_UNREACH (3) /* --> Address unreachable */ +#define ICMPv6_UNREACH_PORT_UNREACH (4) /* --> Port unreachable */ +#define ICMPv6_UNREACH_SRC_ADDR_FAILED (5) /* --> Source address failed ingress/egress policy [RFC4443] */ +#define ICMPv6_UNREACH_REJECT_ROUTE (6) /* --> Reject route to destination [RFC4443] */ +#define ICMPv6_PKTTOOBIG 2 /* Packet too big [RFC 2463, 4443] */ +#define ICMPv6_TIMXCEED 3 /* Time exceeded [RFC 2463, 4443] */ +#define ICMPv6_TIMXCEED_HOP_EXCEEDED (0) /* --> Hop limit exceeded in transit */ +#define ICMPv6_TIMXCEED_REASS_EXCEEDED (1) /* --> Fragment reassembly time exceeded */ +#define ICMPv6_PARAMPROB 4 /* Parameter problem [RFC 2463, 4443] */ +#define ICMPv6_PARAMPROB_FIELD (0) /* --> Erroneous header field encountered */ +#define ICMPv6_PARAMPROB_NEXT_HDR (1) /* --> Unrecognized Next Header type encountered */ +#define ICMPv6_PARAMPROB_OPTION (2) /* --> Unrecognized IPv6 option encountered */ +#define ICMPv6_ECHO 128 /* Echo request [RFC 2463, 4443] */ +#define ICMPv6_ECHOREPLY 129 /* Echo reply [RFC 2463, 4443] */ +#define ICMPv6_GRPMEMBQUERY 130 /* Group Membership Query [RFC 2710] */ +#define ICMPv6_GRPMEMBREP 131 /* Group Membership Report [RFC 2710] */ +#define ICMPv6_GRPMEMBRED 132 /* Group Membership Reduction [RFC 2710] */ +#define ICMPv6_ROUTERSOLICIT 133 /* Router Solicitation [RFC 2461] */ +#define ICMPv6_ROUTERADVERT 134 /* Router Advertisement [RFC 2461] */ +#define ICMPv6_NGHBRSOLICIT 135 /* Neighbor Solicitation [RFC 2461] */ +#define ICMPv6_NGHBRADVERT 136 /* Neighbor Advertisement [RFC 2461] */ +#define ICMPv6_REDIRECT 137 /* Redirect [RFC 2461] */ +#define ICMPv6_RTRRENUM 138 /* Router Renumbering [RFC 2894] */ +#define ICMPv6_RTRRENUM_COMMAND (0) /* --> Router Renumbering Command */ +#define ICMPv6_RTRRENUM_RESULT (1) /* --> Router Renumbering Result */ +#define ICMPv6_RTRRENUM_SEQ_RESET (255) /* Sequence Number Reset */ +#define ICMPv6_NODEINFOQUERY 139 /* ICMP Node Information Query [RFC 4620] */ +#define ICMPv6_NODEINFOQUERY_IPv6ADDR (0) /* --> The Data field contains an IPv6 address */ +#define ICMPv6_NODEINFOQUERY_NAME (1) /* --> The Data field contains a name */ +#define ICMPv6_NODEINFOQUERY_IPv4ADDR (2) /* --> The Data field contains an IPv4 address */ +#define ICMPv6_NODEINFORESP 140 /* ICMP Node Information Response [RFC 4620] */ +#define ICMPv6_NODEINFORESP_SUCCESS (0) /* --> A successful reply. */ +#define ICMPv6_NODEINFORESP_REFUSED (1) /* --> The Responder refuses to supply the answer */ +#define ICMPv6_NODEINFORESP_UNKNOWN (2) /* --> The Qtype of the Query is unknown */ +#define ICMPv6_INVNGHBRSOLICIT 141 /* Inverse Neighbor Discovery Solicitation Message [RFC 3122] */ +#define ICMPv6_INVNGHBRADVERT 142 /* Inverse Neighbor Discovery Advertisement Message [RFC 3122] */ +#define ICMPv6_MLDV2 143 /* MLDv2 Multicast Listener Report [RFC 3810] */ +#define ICMPv6_AGENTDISCOVREQ 144 /* Home Agent Address Discovery Request Message [RFC 3775] */ +#define ICMPv6_AGENTDISCOVREPLY 145 /* Home Agent Address Discovery Reply Message [RFC 3775] */ +#define ICMPv6_MOBPREFIXSOLICIT 146 /* Mobile Prefix Solicitation [RFC 3775] */ +#define ICMPv6_MOBPREFIXADVERT 147 /* Mobile Prefix Advertisement [RFC 3775] */ +#define ICMPv6_CERTPATHSOLICIT 148 /* Certification Path Solicitation [RFC 3971] */ +#define ICMPv6_CERTPATHADVERT 149 /* Certification Path Advertisement [RFC 3971] */ +#define ICMPv6_EXPMOBILITY 150 /* Experimental mobility protocols [RFC 4065] */ +#define ICMPv6_MRDADVERT 151 /* MRD, Multicast Router Advertisement [RFC 4286] */ +#define ICMPv6_MRDSOLICIT 152 /* MRD, Multicast Router Solicitation [RFC 4286] */ +#define ICMPv6_MRDTERMINATE 153 /* MRD, Multicast Router Termination [RFC 4286] */ +#define ICMPv6_FMIPV6 154 /* FMIPv6 messages [RFC 5568] */ + +/* Node Information parameters */ +/* -> Query types */ +#define NI_QTYPE_NOOP 0 +#define NI_QTYPE_UNUSED 1 +#define NI_QTYPE_NODENAME 2 +#define NI_QTYPE_NODEADDRS 3 +#define NI_QTYPE_IPv4ADDRS 4 +/* -> Misc */ +#define NI_NONCE_LEN 8 + +/* Nping ICMPv6Header Class internal definitions */ +#define ICMPv6_COMMON_HEADER_LEN 4 +#define ICMPv6_MIN_HEADER_LEN 8 +#define ICMPv6_UNREACH_LEN (ICMPv6_COMMON_HEADER_LEN+4) +#define ICMPv6_PKTTOOBIG_LEN (ICMPv6_COMMON_HEADER_LEN+4) +#define ICMPv6_TIMXCEED_LEN (ICMPv6_COMMON_HEADER_LEN+4) +#define ICMPv6_PARAMPROB_LEN (ICMPv6_COMMON_HEADER_LEN+4) +#define ICMPv6_ECHO_LEN (ICMPv6_COMMON_HEADER_LEN+4) +#define ICMPv6_ECHOREPLY_LEN (ICMPv6_COMMON_HEADER_LEN+4) +#define ICMPv6_ROUTERSOLICIT_LEN (ICMPv6_COMMON_HEADER_LEN+4) +#define ICMPv6_ROUTERADVERT_LEN (ICMPv6_COMMON_HEADER_LEN+12) +#define ICMPv6_NGHBRSOLICIT_LEN (ICMPv6_COMMON_HEADER_LEN+20) +#define ICMPv6_NGHBRADVERT_LEN (ICMPv6_COMMON_HEADER_LEN+20) +#define ICMPv6_REDIRECT_LEN (ICMPv6_COMMON_HEADER_LEN+36) +#define ICMPv6_RTRRENUM_LEN (ICMPv6_COMMON_HEADER_LEN+12) +#define ICMPv6_NODEINFO_LEN (ICMPv6_COMMON_HEADER_LEN+12) +#define ICMPv6_MLD_LEN (ICMPv6_COMMON_HEADER_LEN+20) +/* This must the MAX() of all values defined above*/ +#define ICMPv6_MAX_MESSAGE_BODY (ICMPv6_REDIRECT_LEN-ICMPv6_COMMON_HEADER_LEN) + + + +/* Node Information flag bitmaks */ +#define ICMPv6_NI_FLAG_T 0x01 +#define ICMPv6_NI_FLAG_A 0x02 +#define ICMPv6_NI_FLAG_C 0x04 +#define ICMPv6_NI_FLAG_L 0x08 +#define ICMPv6_NI_FLAG_G 0x10 +#define ICMPv6_NI_FLAG_S 0x20 + +class ICMPv6Header : public ICMPHeader { + + /**********************************************************************/ + /* COMMON ICMPv6 packet HEADER */ + /**********************************************************************/ + /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + Message Body + + | | */ + struct nping_icmpv6_hdr{ + u8 type; + u8 code; + u16 checksum; + u8 data[ICMPv6_MAX_MESSAGE_BODY]; + }__attribute__((__packed__)); + typedef struct nping_icmpv6_hdr nping_icmpv6_hdr_t; + + + /**********************************************************************/ + /* ICMPv6 MESSAGE SPECIFIC HEADERS */ + /**********************************************************************/ + + /* Destination Unreachable Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | As much of invoking packet | + + as possible without the ICMPv6 packet + + | exceeding the minimum IPv6 MTU [IPv6] | */ + struct dest_unreach_msg{ + u32 unused; + //u8 invoking_pkt[?]; + }__attribute__((__packed__)); + typedef struct dest_unreach_msg dest_unreach_msg_t; + + + /* Packet Too Big Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | MTU | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | As much of invoking packet | + + as possible without the ICMPv6 packet + + | exceeding the minimum IPv6 MTU [IPv6] | */ + struct pkt_too_big_msg{ + u32 mtu; + //u8 invoking_pkt[?]; + }__attribute__((__packed__)); + typedef struct pkt_too_big_msg pkt_too_big_msg_t; + + + /* Time Exceeded Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | As much of invoking packet | + + as possible without the ICMPv6 packet + + | exceeding the minimum IPv6 MTU [IPv6] | */ + struct time_exceeded_msg{ + u32 unused; + //u8 invoking_pkt[?]; + }__attribute__((__packed__)); + typedef struct time_exceeded_msg time_exceeded_msg_t; + + + /* Parameter Problem Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | As much of invoking packet | + + as possible without the ICMPv6 packet + + | exceeding the minimum IPv6 MTU [IPv6] | */ + struct parameter_problem_msg{ + u32 pointer; + //u8 invoking_pkt[?]; + }__attribute__((__packed__)); + typedef struct parameter_problem_msg parameter_problem_msg_t; + + + /* Echo Request/Response Messages + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Identifier | Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Data ... + +-+-+-+-+- */ + struct echo_msg{ + u16 id; + u16 seq; + //u8 data[?]; + }__attribute__((__packed__)); + typedef struct echo_msg echo_msg_t; + + /* Router Advertisement Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Cur Hop Limit |M|O|H|Prf|P|R|R| Router Lifetime | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Reachable Time | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Retrans Timer | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Options ... + +-+-+-+-+-+-+-+-+-+-+-+- */ + struct router_advert_msg{ + u8 current_hop_limit; + u8 autoconfig_flags; /* See RFC 5175 */ + u16 router_lifetime; + u32 reachable_time; + u32 retransmission_timer; + //u8 icmpv6_options[?]; + }__attribute__((__packed__)); + typedef struct router_advert_msg router_advert_msg_t; + + + /* Router Solicitation Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Options ... + +-+-+-+-+-+-+-+-+-+-+-+- */ + struct router_solicit_msg{ + u32 reserved; + //u8 icmpv6_options[?]; + }__attribute__((__packed__)); + typedef struct router_solicit_msg router_solicit_msg_t; + + + /* Neighbor Advertisement Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |R|S|O| Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + + + | | + + Target Address + + | | + + + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Options ... + +-+-+-+-+-+-+-+-+-+-+-+- */ + struct neighbor_advert_msg{ + u8 flags; + u8 reserved[3]; + u8 target_address[16]; + //u8 icmpv6_options[?]; + }__attribute__((__packed__)); + typedef struct neighbor_advert_msg neighbor_advert_msg_t; + + + /* Neighbor Solicitation Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + + + | | + + Target Address + + | | + + + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Options ... + +-+-+-+-+-+-+-+-+-+-+-+- */ + struct neighbor_solicit_msg{ + u32 reserved; + u8 target_address[16]; + //u8 icmpv6_options[?]; + }__attribute__((__packed__)); + typedef struct neighbor_solicit_msg neighbor_solicit_msg_t; + + + /* Redirect Message + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + + + | | + + Target Address + + | | + + + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + + + | | + + Destination Address + + | | + + + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Options ... + +-+-+-+-+-+-+-+-+-+-+-+- */ + struct redirect_msg{ + u32 reserved; + u8 target_address[16]; + u8 destination_address[16]; + //u8 icmpv6_options[?]; + }__attribute__((__packed__)); + typedef struct redirect_msg redirect_msg_t; + + + /* Router Renumbering Header + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SequenceNumber | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SegmentNumber | Flags | MaxDelay | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + / RR Message Body / + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + struct router_renumbering_msg{ + u32 seq; + u8 segment_number; + u8 flags; + u16 max_delay; + u32 reserved; + //u8 rr_msg_body[?]; + }__attribute__((__packed__)); + typedef struct router_renumbering_msg router_renumbering_msg_t; + + + /* Node Information Queries + 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 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Qtype | unused |G|S|L|C|A|T| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + Nonce + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + / Data / + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + struct nodeinfo_msg{ + u16 qtype; + u16 flags; + u64 nonce; + //u8 data[?]; + }__attribute__((__packed__)); + typedef struct nodeinfo_msg nodeinfo_msg_t; + + + /* Multicast Listener Discovery + 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 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Maximum Response Delay | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + + + | | + + Multicast Address + + | | + + + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + struct mld_msg{ + u16 max_response_delay; + u16 reserved; + u8 mcast_address[16]; + }__attribute__((__packed__)); + typedef struct mld_msg mld_msg_t; + + + nping_icmpv6_hdr_t h; + + /* Helper pointers */ + dest_unreach_msg_t *h_du; + pkt_too_big_msg_t *h_ptb; + time_exceeded_msg_t *h_te; + parameter_problem_msg_t *h_pp; + echo_msg_t *h_e; + router_advert_msg_t *h_ra; + router_solicit_msg_t *h_rs; + neighbor_advert_msg_t *h_na; + neighbor_solicit_msg_t *h_ns; + redirect_msg_t *h_r; + router_renumbering_msg_t *h_rr; + nodeinfo_msg_t *h_ni; + mld_msg_t *h_mld; + + public: + ICMPv6Header(); + ~ICMPv6Header(); + void reset(); + u8 *getBufferPointer(); + int storeRecvData(const u8 *buf, size_t len); + int protocol_id() const; + int validate(); + int print(FILE *output, int detail) const; + + /* ICMP Type */ + int setType(u8 val); + u8 getType() const; + bool validateType(); + bool validateType(u8 val); + + /* Code */ + int setCode(u8 c); + u8 getCode() const; + bool validateCode(); + bool validateCode(u8 type, u8 code); + + /* Checksum */ + int setSum(); + int setSum(u16 s); + int setSumRandom(); + u16 getSum() const; + + int setReserved(u32 val); + u32 getReserved() const; + int setUnused(u32 val); + u32 getUnused() const; + + int setFlags(u8 val); + u8 getFlags() const; + + int setMTU(u32 mtu); + u32 getMTU() const; + + /* Parameter problem */ + int setPointer(u32 val); + u32 getPointer() const; + + /* Echo */ + int setIdentifier(u16 val); + u16 getIdentifier() const; + int setSequence(u16 val); + int setSequence(u32 val); + u32 getSequence() const; + + /* Router Advertisement */ + int setCurrentHopLimit(u8 val); + u8 getCurrentHopLimit() const; + + int setRouterLifetime(u16 val); + u16 getRouterLifetime() const; + + int setReachableTime(u32 val); + u32 getReachableTime() const; + + int setRetransmissionTimer(u32 val); + u32 getRetransmissionTimer() const; + + int setTargetAddress(struct in6_addr addr); + struct in6_addr getTargetAddress() const; + + int setDestinationAddress(struct in6_addr addr); + struct in6_addr getDestinationAddress() const; + + int setSegmentNumber(u8 val); + u8 getSegmentNumber() const; + + int setMaxDelay(u16 val); + u16 getMaxDelay() const; + + /* Node Information Queries */ + int setQtype(u16 val); + u16 getQtype() const; + int setNodeInfoFlags(u16 val); + u16 getNodeInfoFlags() const; + int setG(bool flag_value=true); + bool getG() const; + int setS(bool flag_value=true); + bool getS() const; + int setL(bool flag_value=true); + bool getL() const; + int setC(bool flag_value=true); + bool getC() const; + int setA(bool flag_value=true); + bool getA() const; + int setT(bool flag_value=true); + bool getT() const; + int setNonce(u64 nonce_value); + int setNonce(const u8 *nonce); + u64 getNonce() const; + + /* Multicast Listener Discovery */ + int setMulticastAddress(struct in6_addr addr); + struct in6_addr getMulticastAddress() const; + + /* Misc */ + int getHeaderLengthFromType(u8 type) const; + bool isError() const; + const char *type2string(int type, int code) const; + +}; /* End of class ICMPv6Header */ + +#endif diff --git a/libnetutil/ICMPv6Option.cc b/libnetutil/ICMPv6Option.cc new file mode 100644 index 0000000..04976a1 --- /dev/null +++ b/libnetutil/ICMPv6Option.cc @@ -0,0 +1,326 @@ +/*************************************************************************** + * ICMPv6Option.cc -- The ICMPv6Option Class represents an ICMP version 6 * + * option. It contains methods to set any header field. In general, these * + * methods do error checkings and byte order conversion. * + * * + ***********************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 "ICMPv6Option.h" + + +ICMPv6Option::ICMPv6Option() { + this->reset(); +} /* End of ICMPv6Option constructor */ + + +ICMPv6Option::~ICMPv6Option() { + +} /* End of ICMPv6Option destructor */ + + +/** Sets every class attribute to zero */ +void ICMPv6Option::reset(){ + memset(&this->h, 0, sizeof(nping_icmpv6_option_t)); + h_la = (link_addr_option_t *)this->h.data; + h_pi = (prefix_info_option_t *)this->h.data; + h_r = (redirect_option_t *)this->h.data; + h_mtu = (mtu_option_t *)this->h.data; +} /* End of reset() */ + + +/** @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 *ICMPv6Option::getBufferPointer(){ + return (u8*)(&this->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 ICMPv6Option class is able to hold a maximum of + * sizeof(nping_icmpv6_option_t) bytes. If the supplied buffer is longer than + * that, only the first sizeof(nping_icmpv6_option_t) bytes will be stored in + * the internal buffer. + * @warning Supplied len MUST be at least ICMPv6_OPTION_MIN_HEADER_LEN bytes + * @return OP_SUCCESS on success and OP_FAILURE in case of error */ +int ICMPv6Option::storeRecvData(const u8 *buf, size_t len){ + if(buf==NULL || len<ICMPv6_OPTION_MIN_HEADER_LEN){ + return OP_FAILURE; + }else{ + int stored_len = MIN( sizeof(nping_icmpv6_option_t), len); + this->reset(); /* Re-init the object, just in case the caller had used it already */ + this->length=stored_len; + memcpy(&(this->h), buf, stored_len); + } + return OP_SUCCESS; +} /* End of storeRecvData() */ + +int ICMPv6Option::protocol_id() const { + return HEADER_TYPE_ICMPv6_OPTION; +} + + +int ICMPv6Option::setType(u8 val){ + this->h.type=val; + this->length = getHeaderLengthFromType(val); + this->h.length = this->length / 8; + return OP_SUCCESS; +} /* End of setType() */ + +u8 ICMPv6Option::getType(){ + return this->h.type; +} /* End of getType() */ + + +bool ICMPv6Option::validateType(u8 val){ + switch( val ){ + case ICMPv6_OPTION_SRC_LINK_ADDR: + case ICMPv6_OPTION_TGT_LINK_ADDR: + case ICMPv6_OPTION_PREFIX_INFO: + case ICMPv6_OPTION_REDIR_HDR: + case ICMPv6_OPTION_MTU: + return true; + break; + + default: + return false; + break; + } + return false; +} /* End of validateType() */ + + + +int ICMPv6Option::setLength(u8 val){ + this->h.length=val; + return OP_SUCCESS; +} /* End of setLength() */ + +u8 ICMPv6Option::getLength(){ + return this->h.length; +} /* End of getLength() */ + + +int ICMPv6Option::setLinkAddress(u8* val){ + if(val==NULL) + return OP_FAILURE; + switch(this->h.type){ + case ICMPv6_OPTION_SRC_LINK_ADDR: + case ICMPv6_OPTION_TGT_LINK_ADDR: + memcpy(this->h_la->link_addr, val, ICMPv6_OPTION_LINK_ADDRESS_LEN); + return OP_SUCCESS; + break; + + default: + return OP_FAILURE; + break; + } +} /* End of setLinkAddress() */ + + +u8 *ICMPv6Option::getLinkAddress(){ + switch(this->h.type){ + case ICMPv6_OPTION_SRC_LINK_ADDR: + case ICMPv6_OPTION_TGT_LINK_ADDR: + return this->h_la->link_addr; + break; + + default: + return NULL; + break; + } +} /* End of getLinkAddress() */ + + +int ICMPv6Option::setPrefixLength(u8 val){ + if(this->h.type!=ICMPv6_OPTION_PREFIX_INFO) + return OP_FAILURE; + this->h_pi->prefix_length=val; + return OP_SUCCESS; +} /* End of setPrefixLength() */ + + +u8 ICMPv6Option::getPrefixLength(){ + if(this->h.type!=ICMPv6_OPTION_PREFIX_INFO) + return 0; + else + return this->h_pi->prefix_length; +} /* End of getPrefixLength() */ + + +int ICMPv6Option::setFlags(u8 val){ + if(this->h.type!=ICMPv6_OPTION_PREFIX_INFO) + return OP_FAILURE; + this->h_pi->flags=val; + return OP_SUCCESS; +} /* End of setFlags() */ + + +u8 ICMPv6Option::getFlags(){ + if(this->h.type!=ICMPv6_OPTION_PREFIX_INFO) + return 0; + else + return this->h_pi->flags; +} /* End of getFlags() */ + + +int ICMPv6Option::setValidLifetime(u32 val){ + if(this->h.type!=ICMPv6_OPTION_PREFIX_INFO) + return OP_FAILURE; + this->h_pi->valid_lifetime=htonl(val); + return OP_SUCCESS; +} /* End of setValidLifetime() */ + + +u32 ICMPv6Option::getValidLifetime(){ + if(this->h.type!=ICMPv6_OPTION_PREFIX_INFO) + return 0; + else + return ntohl(this->h_pi->valid_lifetime); +} /* End of getValidLifetime() */ + + +int ICMPv6Option::setPreferredLifetime(u32 val){ + if(this->h.type!=ICMPv6_OPTION_PREFIX_INFO) + return OP_FAILURE; + this->h_pi->preferred_lifetime=htonl(val); + return OP_SUCCESS; +} /* End of setPreferredLifetime() */ + + +u32 ICMPv6Option::getPreferredLifetime(){ + if(this->h.type!=ICMPv6_OPTION_PREFIX_INFO) + return 0; + else + return ntohl(this->h_pi->preferred_lifetime); +} /* End of getPreferredLifetime() */ + + +int ICMPv6Option::setPrefix(u8 *val){ + if(val==NULL || this->h.type!=ICMPv6_OPTION_PREFIX_INFO) + return OP_FAILURE; + else + memcpy(this->h_pi->prefix, val, 16); + return OP_SUCCESS; +} /* End of setPrefix() */ + + +u8 *ICMPv6Option::getPrefix(){ + if(this->h.type!=ICMPv6_OPTION_PREFIX_INFO) + return NULL; + else + return this->h_pi->prefix; +} /* End of getPrefix() */ + + +int ICMPv6Option::setMTU(u32 val){ + if(this->h.type!=ICMPv6_OPTION_MTU) + return OP_FAILURE; + this->h_mtu->mtu=htonl(val); + return OP_SUCCESS; +} /* End of setMTU() */ + + +u32 ICMPv6Option::getMTU(){ + if(this->h.type!=ICMPv6_OPTION_MTU) + return 0; + else + return ntohl(this->h_mtu->mtu); +} /* End of getMTU() */ + + + +/******************************************************************************/ +/* MISCELLANEOUS STUFF */ +/******************************************************************************/ + +/** Returns the standard ICMPv6 optiom length for the supplied option type. + * @warning Return value corresponds strictly to the ICMPv7 option header, this + * is, the minimum length of the OPTION, variable length payload is never + * included. For example, an ICMPv6 Redirect option has a fixed header of 8 + * bytes but then it may contain an IPv6 header. We only return 8 + * because we don't know in advance the total number of bytes for the message. + * Same applies to the rest of types. */ +int ICMPv6Option::getHeaderLengthFromType(u8 type){ + switch( type ){ + case ICMPv6_OPTION_SRC_LINK_ADDR: + return ICMPv6_OPTION_SRC_LINK_ADDR_LEN; + break; + + case ICMPv6_OPTION_TGT_LINK_ADDR: + return ICMPv6_OPTION_TGT_LINK_ADDR_LEN; + break; + + case ICMPv6_OPTION_PREFIX_INFO: + return ICMPv6_OPTION_PREFIX_INFO_LEN; + break; + + case ICMPv6_OPTION_REDIR_HDR: + return ICMPv6_OPTION_REDIR_HDR_LEN; + break; + + case ICMPv6_OPTION_MTU: + return ICMPv6_OPTION_MTU_LEN; + break; + + /* Packets with non RFC-Compliant option types will be represented as an + * 8-byte ICMPv6 option. */ + default: + return ICMPv6_OPTION_MIN_HEADER_LEN; + break; + } +} /* End of getHeaderLengthFromType() */ diff --git a/libnetutil/ICMPv6Option.h b/libnetutil/ICMPv6Option.h new file mode 100644 index 0000000..91e1b97 --- /dev/null +++ b/libnetutil/ICMPv6Option.h @@ -0,0 +1,235 @@ +/*************************************************************************** + * ICMPv6Option.h -- The ICMPv6Option Class represents an ICMP version 6 * + * option. It contains methods to set any header field. In general, these * + * methods do error checkings and byte order conversion. * + * * + ***********************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. */ + +#ifndef __ICMPv6OPTION_H__ +#define __ICMPv6OPTION_H__ 1 + +#include "NetworkLayerElement.h" + +/* Packet header diagrams included in this file have been taken from the + * following IETF RFC documents: RFC 2461, RFC 2894 */ + +/* The following codes have been defined by IANA. A complete list may be found + * at http://www.iana.org/assignments/icmpv6-parameters */ + +/* ICMPv6 Option Types */ +#define ICMPv6_OPTION_SRC_LINK_ADDR 1 +#define ICMPv6_OPTION_TGT_LINK_ADDR 2 +#define ICMPv6_OPTION_PREFIX_INFO 3 +#define ICMPv6_OPTION_REDIR_HDR 4 +#define ICMPv6_OPTION_MTU 5 + +/* Nping ICMPv6Options Class internal definitions */ +#define ICMPv6_OPTION_COMMON_HEADER_LEN 2 +#define ICMPv6_OPTION_MIN_HEADER_LEN 8 +#define ICMPv6_OPTION_SRC_LINK_ADDR_LEN (ICMPv6_OPTION_COMMON_HEADER_LEN+6) +#define ICMPv6_OPTION_TGT_LINK_ADDR_LEN (ICMPv6_OPTION_COMMON_HEADER_LEN+6) +#define ICMPv6_OPTION_PREFIX_INFO_LEN (ICMPv6_OPTION_COMMON_HEADER_LEN+30) +#define ICMPv6_OPTION_REDIR_HDR_LEN (ICMPv6_OPTION_COMMON_HEADER_LEN+6) +#define ICMPv6_OPTION_MTU_LEN (ICMPv6_OPTION_COMMON_HEADER_LEN+6) +/* This must the MAX() of all values defined above*/ +#define ICMPv6_OPTION_MAX_MESSAGE_BODY (ICMPv6_OPTION_PREFIX_INFO_LEN-ICMPv6_OPTION_COMMON_HEADER_LEN) + +#define ICMPv6_OPTION_LINK_ADDRESS_LEN 6 + +class ICMPv6Option : public NetworkLayerElement { + + private: + + /**********************************************************************/ + /* COMMON ICMPv6 OPTION HEADER */ + /**********************************************************************/ + + struct nping_icmpv6_option{ + u8 type; + u8 length; + u8 data[ICMPv6_OPTION_MAX_MESSAGE_BODY]; + }__attribute__((__packed__)); + typedef struct nping_icmpv6_option nping_icmpv6_option_t; + + /**********************************************************************/ + /* ICMPv6 OPTION FORMATS */ + /**********************************************************************/ + /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Length | ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ~ ... ~ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + + + /* Source/Target Link-layer Address + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Length | Link-Layer Address ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + struct link_addr_option{ + u8 link_addr[6]; + }__attribute__((__packed__)); + typedef struct link_addr_option link_addr_option_t; + + + /* Prefix Information + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Length | Prefix Length |L|A| Reserved1 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Valid Lifetime | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Preferred Lifetime | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Reserved2 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + + + | | + + Prefix + + | | + + + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + struct prefix_info_option{ + u8 prefix_length; + u8 flags; + u32 valid_lifetime; + u32 preferred_lifetime; + u32 reserved; + u8 prefix[16]; + }__attribute__((__packed__)); + typedef struct prefix_info_option prefix_info_option_t; + + + /* Redirect Header + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Length | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + ~ IP header + data ~ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + struct redirect_option{ + u16 reserved_1; + u32 reserved_2; + //u8 invoking_pkt[?]; + }__attribute__((__packed__)); + typedef struct redirect_option redirect_option_t; + + + /* MTU + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Length | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | MTU | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + struct mtu_option{ + u16 reserved; + u32 mtu; + }__attribute__((__packed__)); + typedef struct mtu_option mtu_option_t; + + + nping_icmpv6_option_t h; + + link_addr_option_t *h_la; + prefix_info_option_t *h_pi; + redirect_option_t *h_r; + mtu_option_t *h_mtu; + + public: + ICMPv6Option(); + ~ICMPv6Option(); + void reset(); + u8 *getBufferPointer(); + int storeRecvData(const u8 *buf, size_t len); + int protocol_id() const; + + int setType(u8 val); + u8 getType(); + bool validateType(u8 val); + + int setLength(u8 val); + u8 getLength(); + + int setLinkAddress(u8* val); + u8 *getLinkAddress(); + + int setPrefixLength(u8 val); + u8 getPrefixLength(); + + int setFlags(u8 val); + u8 getFlags(); + + int setValidLifetime(u32 val); + u32 getValidLifetime(); + + int setPreferredLifetime(u32 val); + u32 getPreferredLifetime(); + + int setPrefix(u8 *val); + u8 *getPrefix(); + + int setMTU(u32 val); + u32 getMTU(); + + int getHeaderLengthFromType(u8 type); + +}; /* End of class ICMPv6Option */ + +#endif diff --git a/libnetutil/ICMPv6RRBody.cc b/libnetutil/ICMPv6RRBody.cc new file mode 100644 index 0000000..8d72cda --- /dev/null +++ b/libnetutil/ICMPv6RRBody.cc @@ -0,0 +1,111 @@ +/*************************************************************************** + * ICMPv6RRBody.h -- The ICMPv6RRBody Class represents an ICMP version 6 * + * Router Renumbering message body. It contains methods to set any header * + * field. In general, these methods do error checkings 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 "ICMPv6RRBody.h" + + +ICMPv6RRBody::ICMPv6RRBody() { + this->reset(); +} /* End of ICMPv6RRBody constructor */ + + +ICMPv6RRBody::~ICMPv6RRBody() { + +} /* End of ICMPv6RRBody destructor */ + + +/** Sets every class attribute to zero */ +void ICMPv6RRBody::reset(){ + memset(&this->h, 0, sizeof(nping_icmpv6_rr_body_t)); + h_mp = (rr_match_prefix_t *)this->h.data; + h_up = (rr_use_prefix_t *)this->h.data; + h_r = (rr_result_msg_t *)this->h.data; +} /* End of reset() */ + + +/** @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 *ICMPv6RRBody::getBufferPointer(){ + return (u8*)(&this->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 ICMPv6RRBody class is able to hold a maximum of + * sizeof(nping_icmpv6_rr_body_t) bytes. If the supplied buffer is longer than + * that, only the first sizeof(nping_icmpv6_rr_body_t) bytes will be stored in + * the internal buffer. + * @warning Supplied len MUST be at least ICMPv6_RR_MIN_LENGTH bytes + * @return OP_SUCCESS on success and OP_FAILURE in case of error */ +int ICMPv6RRBody::storeRecvData(const u8 *buf, size_t len){ + if(buf==NULL || len<ICMPv6_RR_MIN_LENGTH){ + return OP_FAILURE; + }else{ + int stored_len = MIN( sizeof(nping_icmpv6_rr_body_t), len); + this->reset(); /* Re-init the object, just in case the caller had used it already */ + this->length=stored_len; + memcpy(&(this->h), buf, stored_len); + } + return OP_SUCCESS; +} /* End of storeRecvData() */ + diff --git a/libnetutil/ICMPv6RRBody.h b/libnetutil/ICMPv6RRBody.h new file mode 100644 index 0000000..4f6deff --- /dev/null +++ b/libnetutil/ICMPv6RRBody.h @@ -0,0 +1,200 @@ +/*************************************************************************** + * ICMPv6RRBody.cc -- The ICMPv6RRBody Class represents an ICMP version 6 * + * Router Renumbering message body. It contains methods to set any header * + * field. In general, these methods do error checkings 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. */ + +#ifndef ICMPv6RRBODY_H +#define ICMPv6RRBODY_H 1 + +#include "NetworkLayerElement.h" + +/* Packet header diagrams included in this file have been taken from the + * following IETF RFC documents: RFC 2894 */ + +/* Nping ICMPv6RRBody Class internal definitions */ +#define ICMPv6_RR_MATCH_PREFIX_LEN 24 +#define ICMPv6_RR_USE_PREFIX_LEN 32 +#define ICMPv6_RR_RESULT_MSG_LEN 24 +/* This must the MAX() of all values defined above*/ +#define ICMPv6_RR_MAX_LENGTH (ICMPv6_RR_USE_PREFIX_LEN) +#define ICMPv6_RR_MIN_LENGTH (ICMPv6_RR_MATCH_PREFIX_LEN) + + +class ICMPv6RRBody : public NetworkLayerElement { + + private: + + /**********************************************************************/ + /* COMMON ICMPv6 OPTION HEADER */ + /**********************************************************************/ + + struct nping_icmpv6_rr_body{ + u8 data[ICMPv6_RR_MAX_LENGTH]; + }__attribute__((__packed__)); + typedef struct nping_icmpv6_rr_body nping_icmpv6_rr_body_t; + + /**********************************************************************/ + /* ICMPv6 OPTION FORMATS */ + /**********************************************************************/ + + + /* Match-Prefix Part + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | OpCode | OpLength | Ordinal | MatchLen | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | MinLen | MaxLen | reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + +- -+ + | | + +- MatchPrefix -+ + | | + +- -+ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + struct rr_match_prefix{ + u8 op_code; + u8 op_length; + u8 ordinal; + u8 match_length; + u8 min_length; + u8 max_length; + u16 reserved; + u8 match_prefix[16]; + }__attribute__((__packed__)); + typedef struct rr_match_prefix rr_match_prefix_t; + + + /* Use-Prefix Part + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | UseLen | KeepLen | FlagMask | RAFlags | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Valid Lifetime | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Preferred Lifetime | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |V|P| reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + +- -+ + | | + +- UsePrefix -+ + | | + +- -+ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + struct rr_use_prefix{ + u8 use_len; + u8 keep_len; + u8 flag_mask; + u8 ra_flags; + u32 valid_lifetime; + u32 preferred_lifetime; + u8 flags; + u8 reserved[3]; + u8 use_prefix[16]; + }__attribute__((__packed__)); + typedef struct rr_use_prefix rr_use_prefix_t; + + + /* Result Message + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | reserved |B|F| Ordinal | MatchedLen | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | InterfaceIndex | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + +- -+ + | | + +- MatchedPrefix -+ + | | + +- -+ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + struct rr_result_msg{ + u8 reserved; + u8 flags; + u8 ordinal; + u8 matched_length; + u32 interface_index; + u8 matched_prefix[16]; + }__attribute__((__packed__)); + typedef struct rr_result_msg rr_result_msg_t; + + nping_icmpv6_rr_body_t h; + + rr_match_prefix_t *h_mp; + rr_use_prefix_t *h_up; + rr_result_msg_t *h_r; + + public: + ICMPv6RRBody(); + ~ICMPv6RRBody(); + void reset(); + u8 *getBufferPointer(); + int storeRecvData(const u8 *buf, size_t len); + +}; /* End of class ICMPv6RRBody */ + +#endif diff --git a/libnetutil/IPv4Header.cc b/libnetutil/IPv4Header.cc new file mode 100644 index 0000000..b206902 --- /dev/null +++ b/libnetutil/IPv4Header.cc @@ -0,0 +1,634 @@ +/*************************************************************************** + * IPv4Header.cc -- The IPv4Header Class represents an IPv4 datagram. It * + * contains methods to set any header field. In general, these methods do * + * error checkings and byte order conversion. * + * * + ***********************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 "IPv4Header.h" +#include <assert.h> + +/******************************************************************************/ +/* CONTRUCTORS, DESTRUCTORS AND INITIALIZATION METHODS */ +/******************************************************************************/ +IPv4Header::IPv4Header() { + this->reset(); +} /* End of IPv4Header constructor */ + + +IPv4Header::~IPv4Header() { + +} /* End of IPv4Header destructor */ + + +/** Sets every attribute to its default value */ +void IPv4Header::reset() { + memset(&this->h, 0, sizeof(nping_ipv4_hdr_t)); + this->ipoptlen=0; + this->length=20; /* Initial value 20. This will be incremented if options are used */ + this->setVersion(); + this->setHeaderLength(); + this->setTOS(IPv4_DEFAULT_TOS); + this->setIdentification(IPv4_DEFAULT_ID); + this->setTTL(IPv4_DEFAULT_TTL); + this->setNextProto(IPv4_DEFAULT_PROTO); + this->setTotalLength(); +} /* End of IPv4Header destructor */ + + +/******************************************************************************/ +/* 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 *IPv4Header::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 IPv4Header 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 IP header length). + * @return OP_SUCCESS on success and OP_FAILURE in case of error */ +int IPv4Header::storeRecvData(const u8 *buf, size_t len){ + if(buf==NULL || len<IP_HEADER_LEN){ + return OP_FAILURE; + }else{ + int stored_len = MIN((IP_HEADER_LEN + MAX_IP_OPTIONS_LEN), len); + this->reset(); /* Re-init the object, just in case the caller had used it already */ + this->length=stored_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 IPv4Header::protocol_id() const { + return HEADER_TYPE_IPv4; +} /* End of protocol_id() */ + + +/** Performs some VERY BASIC checks that intend to validate the information + * stored in the internal buffer, as a valid protocol header. + * @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 IPv4Header::validate(){ + if(this->getVersion()!=4) + return OP_FAILURE; + else if( this->getHeaderLength()<5) + return OP_FAILURE; + else if( this->getHeaderLength()*4 > this->length) + return OP_FAILURE; + this->length=this->getHeaderLength()*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 IPv4Header::print(FILE *output, int detail) const { + static char ipstring[256]; + memset(ipstring, 0, 256); + struct in_addr addr; + int frag_off = 8 * this->getFragOffset() & 8191; /* 2^13 - 1 */; + char ipinfo[512] = ""; /* Temp info about IP. */ + char fragnfo[64] = ""; /* Temp info about fragmentation. */ + + fprintf(output, "IPv4["); + + this->getSourceAddress(&addr); + inet_ntop(AF_INET, &addr, ipstring, sizeof(ipstring)); + fprintf(output, "%s", ipstring); + + fprintf(output, " >"); + + this->getDestinationAddress(&addr); + inet_ntop(AF_INET, &addr, ipstring, sizeof(ipstring)); + fprintf(output, " %s", ipstring); + + /* Is this a fragmented packet? is it the last fragment? */ + if (frag_off || this->getMF()) { + Snprintf(fragnfo, sizeof(fragnfo), " frag offset=%d%s", frag_off, this->getMF() ? "+" : ""); + } + + /* Create a string with information relevant to the specified level of detail */ + if( detail == PRINT_DETAIL_LOW ){ + Snprintf(ipinfo, sizeof(ipinfo), "ttl=%d id=%d iplen=%d%s%s%s%s", + this->getTTL(), this->getIdentification(), this->getTotalLength(), fragnfo, + this->getHeaderLength()==5?"":" ipopts={", + this->getHeaderLength()?"":format_ip_options(this->h.options , MIN(this->getHeaderLength()*4, this->length-IP_HEADER_LEN)), + this->getHeaderLength()?"":"}"); + }else if( detail == PRINT_DETAIL_MED ){ + Snprintf(ipinfo, sizeof(ipinfo), "ttl=%d id=%d proto=%d csum=0x%04X iplen=%d%s%s%s%s", + this->getTTL(), this->getIdentification(), + this->getNextProto(), this->getSum(), + this->getTotalLength(), fragnfo, + this->getHeaderLength()==5?"":" ipopts={", + this->getHeaderLength()==5?"":format_ip_options(this->h.options , MIN(this->getHeaderLength()*4, this->length-IP_HEADER_LEN)), + this->getHeaderLength()==5?"":"}"); + }else if( detail>=PRINT_DETAIL_HIGH ){ + Snprintf(ipinfo, sizeof(ipinfo), "ver=%d ihl=%d tos=0x%02x iplen=%d id=%d%s%s%s%s foff=%d%s ttl=%d proto=%d csum=0x%04X%s%s%s", + this->getVersion(), this->getHeaderLength(), + this->getTOS(), this->getTotalLength(), + this->getIdentification(), + (this->getRF() ||this->getDF()||this->getMF()) ? " flg=" : "", + (this->getRF()) ? "x" : "", + (this->getDF() )? "D" : "", + (this->getMF() )? "M": "", + frag_off, (this->getMF()) ? "+" : "", + this->getTTL(), this->getNextProto(), + this->getSum(), + this->getHeaderLength()==5?"":" ipopts={", + this->getHeaderLength()==5?"":format_ip_options(this->h.options , MIN(this->getHeaderLength()*4, this->length-IP_HEADER_LEN)), + this->getHeaderLength()==5?"":"}"); + } + + fprintf(output, " %s]", ipinfo); + + if(this->next!=NULL){ + print_separator(output, detail); + next->print(output, detail); + } + return OP_SUCCESS; +} /* End of print() */ + + +/******************************************************************************/ +/* PROTOCOL-SPECIFIC METHODS */ +/******************************************************************************/ + +int IPv4Header::setVersion(){ + h.ip_v = 4; + return 4; +} /* End of setVersion() */ + + +u8 IPv4Header::getVersion() const { + return (u8)h.ip_v; +} /* End of getVersion() */ + + +int IPv4Header::setHeaderLength(){ + h.ip_hl = 5 + (ipoptlen/4); + return OP_SUCCESS; +} /* End of setHeaderLength() */ + + +int IPv4Header::setHeaderLength(u8 l){ + h.ip_hl = l; + return OP_SUCCESS; +} /* End of setHeaderLength() */ + + +u8 IPv4Header::getHeaderLength() const { + return h.ip_hl; +} /* End of getHeaderLength() */ + + +int IPv4Header::setTOS(u8 v){ + h.ip_tos = v; + return OP_SUCCESS; +} /* End of setTOS() */ + + +u8 IPv4Header::getTOS() const { + return h.ip_tos; +} /* End of getTOS() */ + + +int IPv4Header::setTotalLength(){ + int mylen = 4*getHeaderLength(); + int otherslen=0; + + if (next!=NULL) + otherslen=next->getLen(); + h.ip_len=htons( mylen+otherslen ); + return OP_SUCCESS; +} /* End of setTotalLength() */ + + +/** @warning Supplied value MUST be in host byte order because it will get + * converted by this method using htons() */ +int IPv4Header::setTotalLength(u16 l){ + h.ip_len = htons(l); + return OP_SUCCESS; +} /* End of setTotalLength() */ + + +/** @warning Returned value is already in host byte order. */ +u16 IPv4Header::getTotalLength() const { + return ntohs(h.ip_len); +} /* End of getTotalLength() */ + + +/** Sets identification field to a random value */ +int IPv4Header::setIdentification(){ + h.ip_id=get_random_u16(); + return OP_SUCCESS; +} /* End of setIdentification() */ + + +/** @warning Supplied value MUST be in host byte order because it will get + * converted by this method using htons() */ +int IPv4Header::setIdentification(u16 i){ + h.ip_id = htons(i); + return OP_SUCCESS; +} /* End of setIdentification() */ + + +/** @warning Returned value is already in host byte order. */ +u16 IPv4Header::getIdentification() const { + return ntohs(h.ip_id); +} /* End of getIdentification() */ + + +/** Sets fragment offset field to a random value */ +int IPv4Header::setFragOffset(){ + /* TODO: Should we check here that i<8192 ? */ + h.ip_off=get_random_u16(); + return OP_SUCCESS; +} /* End of setFragOffset() */ + + +/** @warning Supplied value MUST be in host byte order because it will get + * converted by this method using htons() */ +int IPv4Header::setFragOffset(u16 i){ + /* TODO: Should we check here that i<8192 ? */ + h.ip_off = htons(i); + return OP_SUCCESS; +} /* End of setFragOffset() */ + + +/** @warning Returned value is already in host byte order. */ +u16 IPv4Header::getFragOffset() const { + return ntohs(h.ip_off); +} /* End of getFragOffset() */ + + +/** Set RF flag */ +int IPv4Header::setRF(){ + h.ip_off |= htons(IP_RF); + return OP_SUCCESS; +} /* End of setRF() */ + +/** Unset RF flag */ +int IPv4Header::unsetRF(){ + h.ip_off = h.ip_off & ~(htons(IP_RF)); + return OP_SUCCESS; +} /* End of unsetRF() */ + + +/** Get RF flag */ +bool IPv4Header::getRF() const { + return h.ip_off & htons(IP_RF); +} /* End of getRF() */ + + +/** Set MF flag */ +int IPv4Header::setMF(){ + h.ip_off |= htons(IP_MF); + return OP_SUCCESS; +} /* End of setMF() */ + + +/** Unset MF flag */ +int IPv4Header::unsetMF(){ + h.ip_off = h.ip_off & ~(htons(IP_MF)); + return OP_SUCCESS; +} /* End of unsetMF() */ + + +/* Get MF flag */ +bool IPv4Header::getMF() const { + return h.ip_off & htons(IP_MF); +} /* End of getMF() */ + + +/** Set DF flag */ +int IPv4Header::setDF(){ + h.ip_off |= htons(IP_DF); + return OP_SUCCESS; +} /* End of setDF() */ + + +/** Unset DF flag */ +int IPv4Header::unsetDF(){ + h.ip_off = h.ip_off & ~(htons(IP_DF)); + return OP_SUCCESS; +} /* End of unsetDF() */ + + +/** Get DF flag */ +bool IPv4Header::getDF() const { + return h.ip_off & htons(IP_DF); +} /* End of getDF) */ + + +/** Sets TTL field to a random value */ +int IPv4Header::setTTL(){ + h.ip_ttl=get_random_u8(); + return OP_SUCCESS; +} /* End of setTTL() */ + + +/** @warning Supplied value MUST be in host byte order because it will get + * converted by this method using htons() */ +int IPv4Header::setTTL(u8 t){ + h.ip_ttl = t; + return OP_SUCCESS; +} /* End of setTTL() */ + + +/** @warning Returned value is already in host byte order. */ +u8 IPv4Header::getTTL() const { + return h.ip_ttl; +} /* End of getTTL() */ + + +/** Sets field "next protocol" to the supplied value. + * @warning: No error checks are made. Make sure the supplied value + * corresponds to an actual IANA number. Check + * http://www.iana.org/assignments/protocol-numbers/ for more details. */ +int IPv4Header::setNextProto(u8 p){ + h.ip_p = p; + return OP_SUCCESS; +} /* End of setNextProto() */ + + +/** Sets field "next protocol" to the number that corresponds to the supplied + * protocol name. Currently only TCP, UDP and ICMP are supported. Any + * help to extend this functionality would be appreciated. For a list of all + * proto names and numbers check: + * http://www.iana.org/assignments/protocol-numbers/ */ +int IPv4Header::setNextProto(const char *p){ + if (p==NULL){ + printf("setNextProto(): NULL pointer supplied\n"); + return OP_FAILURE; + } + if( !strcasecmp(p, "TCP") ) + h.ip_p=6; /* 6=IANA number for proto TCP */ + + else if( !strcasecmp(p, "UDP") ) + h.ip_p=17; /* 17=IANA number for proto UDP */ + + else if( !strcasecmp(p, "ICMP") ) + h.ip_p=1; /* 1=IANA number for proto ICMP */ + else{ + printf("setNextProto(): Invalid protocol number\n"); + return OP_FAILURE; + } + return OP_SUCCESS; +} /* End of setNextProto() */ + + +/** Returns next protocol number */ +u8 IPv4Header::getNextProto() const { + return h.ip_p; +} /* End of getNextProto() */ + + +u8 IPv4Header::getNextHeader() const { + return this->getNextProto(); +} /* End of getNextHeader() */ + + +int IPv4Header::setNextHeader(u8 val){ + return this->setNextProto(val); +} /* End of setNextHeader() */ + + +/** Computes the IPv4 header checksum and sets the ip_sum field to the right + * value. */ +int IPv4Header::setSum(){ + h.ip_sum = 0; + /* ip_checksum() comes from libdnet */ + ip_checksum((void*)&h, 20 + ipoptlen ); + return OP_SUCCESS; +} /* End of setSum() */ + + +/** @warning Sum is set to supplied value with NO byte ordering conversion + * performed. + * @warning If sum is supplied this way, no error checks are made. Caller is + * responsible for the correctness of the value. */ +int IPv4Header::setSum(u16 s){ + h.ip_sum = s; + return OP_SUCCESS; +} /* End of setSum() */ + + +/** Set the checksum field to a random value */ +int IPv4Header::setSumRandom(){ + h.ip_sum=get_random_u16(); + return OP_SUCCESS; +} /* End of setRandomSum() */ + + +/** Returns the value of the checksum field. + * @warning The returned value is in NETWORK byte order, no conversion is + * performed */ +u16 IPv4Header::getSum() const { + return h.ip_sum; +} /* End of getSum() */ + + +/** Sets destination IP address. + * @warning Destination IP must be supplied in NETWORK byte order. Usually + * all regular library functions return IPs in network byte order so there + * should be no need to worry. */ +int IPv4Header::setDestinationAddress(u32 d){ + h.ip_dst.s_addr = d; + return OP_SUCCESS; +} /* End of setDestinationAddress() */ + +/** Sets destination IP address. + * @warning Destination IP must be supplied in NETWORK byte order. Usually + * all regular library functions return IPs in network byte order so there + * should be no need to worry. */ +int IPv4Header::setDestinationAddress(struct in_addr d){ + h.ip_dst=d; + return OP_SUCCESS; +} /* End of setDestinationAddress() */ + + +/** Returns destination IP address. + * @warning Returned value is in NETWORK byte order. */ +const u8 *IPv4Header::getDestinationAddress() const { + return (u8 *)(&h.ip_dst.s_addr); +} /* End of getDestinationAddress() */ + + +/** Returns destination IP address. + * @warning Returned value is in NETWORK byte order. */ +struct in_addr IPv4Header::getDestinationAddress(struct in_addr *result) const { + if(result!=NULL) + *result=this->h.ip_dst; + return h.ip_dst; +} /* End of getDestinationAddress() */ + + +/** Sets source IP address. + * @warning Destination IP must be supplied in NETWORK byte order. Usually + * all regular library functions return IPs in network byte order so there + * should be no need to worry. */ +int IPv4Header::setSourceAddress(u32 d){ + h.ip_src.s_addr = d; + return OP_SUCCESS; +} /* End of setSourceAddress() */ + + +/** Sets source IP address. + * @warning Destination IP must be supplied in NETWORK byte order. Usually + * all regular library functions return IPs in network byte order so there + * should be no need to worry. */ +int IPv4Header::setSourceAddress(struct in_addr d){ + h.ip_src=d; + return OP_SUCCESS; +} /* End of setSourceAddress() */ + + +/** Returns source ip + * @warning Returned value is in NETWORK byte order. */ +const u8 *IPv4Header::getSourceAddress() const { + return (u8 *)(&h.ip_src.s_addr); +} /* End of getSourceAddress() */ + + +/** Returns source ip + * @warning Returned value is in NETWORK byte order. */ +struct in_addr IPv4Header::getSourceAddress(struct in_addr *result) const { + if(result!=NULL) + *result=this->h.ip_src; + return h.ip_src; +} /* End of getSourceAddress() */ + + +/** Returns the length of an IPv4 address. */ +u16 IPv4Header::getAddressLength() const { + return 4; +} /* End of getAddressLength()*/ + + +int IPv4Header::setOpts(const char *txt){ + int foo=0; + int bar=0; + int ret=0; + u8 buffer[128]; + char errstr[256]; + + if(txt==NULL){ + printf("setOpts(): NULL pointer supplied.\n"); + return OP_FAILURE; + } + + /* Parse IP options */ + if((ret=parse_ip_options(txt, buffer, 128, &foo, &bar, errstr, sizeof(errstr)))==OP_FAILURE){ + printf("%s\n", errstr); + return OP_FAILURE; + }else{ + /* Copy options to our IP header */ + this->setOpts(buffer, ret); + } + return OP_SUCCESS; +} /* End of setOpts() */ + + +int IPv4Header::setOpts(u8 *opts_buff, u32 opts_len){ + if(opts_buff==NULL || opts_len==0) + return OP_FAILURE; + assert(opts_len<=MAX_IP_OPTIONS_LEN); /* Max length for IP options */ + memcpy(this->h.options, opts_buff, opts_len); + this->ipoptlen=opts_len; + this->length += opts_len; + this->setHeaderLength(); + return OP_SUCCESS; +} /* End of setOpts() */ + + +const u8 *IPv4Header::getOpts() const { + return h.options; +} /* End of getOpts() */ + + +const u8 *IPv4Header::getOpts(int *len) const { + if(len==NULL) + printf("getOpts(): NULL pointer supplied.\n"); + else + *len=ipoptlen; + return h.options; +} /* End of getOpts() */ + + +int IPv4Header::printOptions() const { + char *p=format_ip_options(this->h.options, this->ipoptlen); + printf("%s", p); + return OP_SUCCESS; +} /* End of printOptions() */ + +const char *IPv4Header::getOptionsString() const { + return format_ip_options(this->h.options, this->ipoptlen); +} /* End of getOptionsString() */ diff --git a/libnetutil/IPv4Header.h b/libnetutil/IPv4Header.h new file mode 100644 index 0000000..5541000 --- /dev/null +++ b/libnetutil/IPv4Header.h @@ -0,0 +1,220 @@ +/*************************************************************************** + * IPv4Header.h -- The IPv4Header Class represents an IPv4 datagram. It * + * contains methods to set any header field. In general, these methods do * + * error checkings and byte order conversion. * + * * + ***********************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. */ + +#ifndef IPV4HEADER_H +#define IPV4HEADER_H 1 + +#include "NetworkLayerElement.h" + +#define IP_RF 0x8000 /* Reserved fragment flag */ +#define IP_DF 0x4000 /* Don't fragment flag */ +#define IP_MF 0x2000 /* More fragments flag */ +#define IP_OFFMASK 0x1fff /* Mask for fragmenting bits */ +#define IP_HEADER_LEN 20 /* Length of the standard header */ +#define MAX_IP_OPTIONS_LEN 40 /* Max Length for IP Options */ + +/* Default header values */ +#define IPv4_DEFAULT_TOS 0 +#define IPv4_DEFAULT_ID 0 +#define IPv4_DEFAULT_TTL 64 +#define IPv4_DEFAULT_PROTO 6 /* TCP */ + +class IPv4Header : public NetworkLayerElement { + + private: + /* + 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 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |Version| IHL |Type of Service| Total Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Identification |Flags| Fragment Offset | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Time to Live | Protocol | Header Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Source Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Destination Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Options | Padding | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + struct nping_ipv4_hdr { + #if WORDS_BIGENDIAN + u8 ip_v:4; /* Version */ + u8 ip_hl:4; /* Header length */ + #else + u8 ip_hl:4; /* Header length */ + u8 ip_v:4; /* Version */ + #endif + u8 ip_tos; /* Type of service */ + u16 ip_len; /* Total length */ + u16 ip_id; /* Identification */ + u16 ip_off; /* Fragment offset field */ + u8 ip_ttl; /* Time to live */ + u8 ip_p; /* Protocol */ + u16 ip_sum; /* Checksum */ + struct in_addr ip_src; /* Source IP address */ + struct in_addr ip_dst; /* Destination IP address */ + u8 options[MAX_IP_OPTIONS_LEN]; /* IP Options */ + }__attribute__((__packed__)); + + typedef struct nping_ipv4_hdr nping_ipv4_hdr_t; + + nping_ipv4_hdr_t h; + + int ipoptlen; /**< Length of IP options */ + + public: + + /* Misc */ + IPv4Header(); + ~IPv4Header(); + void reset(); + u8 *getBufferPointer(); + int storeRecvData(const u8 *buf, size_t len); + int protocol_id() const; + int validate(); + int print(FILE *output, int detail) const; + + /* IP version */ + int setVersion(); + u8 getVersion() const; + + /* Header Length */ + int setHeaderLength(); + int setHeaderLength(u8 l); + u8 getHeaderLength() const; + + /* Type of Service */ + int setTOS(u8 v); + u8 getTOS() const; + + /* Total length of the datagram */ + int setTotalLength(); + int setTotalLength(u16 l); + u16 getTotalLength() const; + + /* Identification value */ + int setIdentification(); + int setIdentification(u16 i); + u16 getIdentification() const; + + /* Fragment Offset */ + int setFragOffset(); + int setFragOffset(u16 f); + u16 getFragOffset() const; + + /* Flags */ + int setRF(); + int unsetRF(); + bool getRF() const; + int setDF(); + int unsetDF(); + bool getDF() const; + int setMF(); + int unsetMF(); + bool getMF() const; + + /* Time to live */ + int setTTL(); + int setTTL(u8 t); + u8 getTTL() const; + + /* Next protocol */ + int setNextProto(u8 p); + int setNextProto(const char *p); + u8 getNextProto() const; + int setNextHeader(u8 val); + u8 getNextHeader() const; + + /* Checksum */ + int setSum(); + int setSum(u16 s); + int setSumRandom(); + u16 getSum() const; + + /* Destination IP */ + int setDestinationAddress(u32 d); + int setDestinationAddress(struct in_addr d); + const u8 *getDestinationAddress() const; + struct in_addr getDestinationAddress(struct in_addr *result) const; + + + /* Source IP */ + int setSourceAddress(u32 d); + int setSourceAddress(struct in_addr d); + const u8 *getSourceAddress() const; + struct in_addr getSourceAddress(struct in_addr *result) const; + + u16 getAddressLength() const; + + /* IP Options */ + int setOpts(const char *txt); + int setOpts(u8 *opts_buff, u32 opts_len); + const u8 *getOpts() const; + const u8 *getOpts(int *len) const; + int printOptions() const; + const char *getOptionsString() const; + +}; /* End of class IPv4Header */ + +#endif diff --git a/libnetutil/IPv6ExtensionHeader.h b/libnetutil/IPv6ExtensionHeader.h new file mode 100644 index 0000000..54c191f --- /dev/null +++ b/libnetutil/IPv6ExtensionHeader.h @@ -0,0 +1,81 @@ +/*************************************************************************** + * IPv6ExtensionHeader.h -- The IPv6ExtensionHeader class represents * + * a generic class for IPv6 extension headers. Specific headers (like * + * Hop-by-Hop or Routing) inherit from this class. * + ***********************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. */ + +#ifndef __IPv6_EXTENSION_HEADER_H__ +#define __IPv6_EXTENSION_HEADER_H__ 1 + +#include "PacketElement.h" + +/* Extension header option codes */ +#define EXTOPT_PAD1 0x00 /* Pad1 (RFC 2460) */ +#define EXTOPT_PADN 0x01 /* PadN (RFC 2460) */ +#define EXTOPT_JUMBO 0xC2 /* Jumbo Payload (RFC 2675) */ +#define EXTOPT_TUNENCAPLIM 0x04 /* Tunnel Encapsulation Limit (RFC 2473) */ +#define EXTOPT_ROUTERALERT 0x05 /* Router Alert (RFC 2711) */ +#define EXTOPT_QUICKSTART 0x26 /* Quick-Start (RFC 4782) */ +#define EXTOPT_CALIPSO 0x07 /* CALIPSO (RFC 5570) */ +#define EXTOPT_HOMEADDR 0xC9 /* Home Address (RFC 6275) */ + +class IPv6ExtensionHeader : public PacketElement { + +}; + +#endif + diff --git a/libnetutil/IPv6Header.cc b/libnetutil/IPv6Header.cc new file mode 100644 index 0000000..8035a98 --- /dev/null +++ b/libnetutil/IPv6Header.cc @@ -0,0 +1,505 @@ +/*************************************************************************** + * IPv6Header.cc -- The IPv6Header Class represents an IPv4 datagram. It * + * contains methods to set any header field. In general, these methods do * + * error checkings and byte order conversion. * + * * + ***********************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 "IPv6Header.h" + +/******************************************************************************/ +/* CONTRUCTORS, DESTRUCTORS AND INITIALIZATION METHODS */ +/******************************************************************************/ +IPv6Header::IPv6Header() { + this->reset(); +} /* End of IPv6Header constructor */ + + +IPv6Header::~IPv6Header() { + +} /* End of IPv6Header destructor */ + + +/** Sets every attribute to its default value */ +void IPv6Header::reset(){ + memset(&this->h, 0, sizeof(nping_ipv6_hdr_t)); + this->length=IPv6_HEADER_LEN; + this->setVersion(); + this->setTrafficClass(IPv6_DEFAULT_TCLASS); + this->setFlowLabel(IPv6_DEFAULT_FLABEL); + this->setHopLimit(IPv6_DEFAULT_HOPLIM); + this->setNextHeader(IPv6_DEFAULT_NXTHDR); /* No next header */ + this->setPayloadLength(0); +} /* 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 *IPv6Header::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 IPv6Header class is able to hold a maximum of 40 bytes. If the + * supplied buffer is longer than that, only the first 40 bytes will be stored + * in the internal buffer. + * @warning Supplied len MUST be at least 40 bytes (IPv6 header length). + * @return OP_SUCCESS on success and OP_FAILURE in case of error */ +int IPv6Header::storeRecvData(const u8 *buf, size_t len){ + if(buf==NULL || len<IPv6_HEADER_LEN){ + return OP_FAILURE; + }else{ + this->reset(); /* Re-init the object, just in case the caller had used it already */ + this->length=IPv6_HEADER_LEN; + memcpy(&(this->h), buf, IPv6_HEADER_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 IPv6Header::protocol_id() const { + return HEADER_TYPE_IPv6; +} /* 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. + * @return the length, in bytes, of the header, if its found to be valid or + * OP_FAILURE (-1) otherwise. */ +int IPv6Header::validate(){ + if( this->length!=IPv6_HEADER_LEN) + return OP_FAILURE; + else + return IPv6_HEADER_LEN; +} /* 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 IPv6Header::print(FILE *output, int detail) const { + static char ipstring[256]; + memset(ipstring, 0, 256); + struct in6_addr addr; + char ipinfo[512] = ""; /* Temp info about IP. */ + + fprintf(output, "IPv6["); + this->getSourceAddress(&addr); + inet_ntop(AF_INET6, &addr, ipstring, sizeof(ipstring)); + fprintf(output, "%s", ipstring); + fprintf(output, " >"); + this->getDestinationAddress(&addr); + inet_ntop(AF_INET6, &addr, ipstring, sizeof(ipstring)); + fprintf(output, " %s", ipstring); + + /* Create a string with information relevant to the specified level of detail */ + if( detail == PRINT_DETAIL_LOW ){ + Snprintf(ipinfo, sizeof(ipinfo), "hlim=%d", this->getHopLimit()); + }else if( detail == PRINT_DETAIL_MED ){ + Snprintf(ipinfo, sizeof(ipinfo), "hlim=%d tclass=%d flow=%d", + this->getHopLimit(), this->getTrafficClass(), this->getFlowLabel() ); + }else if( detail>=PRINT_DETAIL_HIGH ){ + Snprintf(ipinfo, sizeof(ipinfo), "ver=%d hlim=%d tclass=%d flow=%d plen=%d nh=%d", + this->getVersion(), this->getHopLimit(), this->getTrafficClass(), + this->getFlowLabel(), this->getPayloadLength(), this->getNextHeader() ); + } + fprintf(output, " %s]", ipinfo); + if(this->next!=NULL){ + print_separator(output, detail); + next->print(output, detail); + } + return OP_SUCCESS; +} /* End of print() */ + + +/******************************************************************************/ +/* PROTOCOL-SPECIFIC METHODS */ +/******************************************************************************/ + +/** Set Version field (4 bits). */ +int IPv6Header::setVersion(u8 val){ + union{ + struct firstbyte{ + #if WORDS_BIGENDIAN + u8 ver:4; + u8 tclass:4; + #else + u8 tclass:4; + u8 ver:4; + #endif + }halfbyte; + u8 fullbyte; + }header1stbyte; + + header1stbyte.fullbyte = h.ip6_start[0]; + header1stbyte.halfbyte.ver=val; + h.ip6_start[0]=header1stbyte.fullbyte; + return OP_SUCCESS; +} /* End of setVersion() */ + + +/** Set Version field to value 6. */ +int IPv6Header::setVersion(){ + this->setVersion(6); + return OP_SUCCESS; +} /* End of setVersion() */ + + +/** Returns an 8bit number containing the value of the Version field. */ +u8 IPv6Header::getVersion() const { + union{ + struct firstbyte{ + #if WORDS_BIGENDIAN + u8 ver:4; + u8 tclass:4; + #else + u8 tclass:4; + u8 ver:4; + #endif + }halfbyte; + u8 fullbyte; + }header1stbyte; + + header1stbyte.fullbyte = h.ip6_start[0]; + return (u8)header1stbyte.halfbyte.ver; +} /* End of getVersion() */ + + +int IPv6Header::setTrafficClass(u8 val){ + union{ + struct firstbyte{ + #if WORDS_BIGENDIAN + u8 ver:4; + u8 tclass1:4; + #else + u8 tclass1:4; + u8 ver:4; + #endif + }halfbyte; + u8 fullbyte; + }header1stbyte; + union{ + struct firstbyte{ + #if WORDS_BIGENDIAN + u8 tclass2:4; + u8 flow:4; + #else + u8 flow:4; + u8 tclass2:4; + #endif + }halfbyte; + u8 fullbyte; + }header2ndbyte; + + /* Store old contents */ + header1stbyte.fullbyte = h.ip6_start[0]; + header2ndbyte.fullbyte = h.ip6_start[1]; + + /* Fill the two 4bit halves */ + header1stbyte.halfbyte.tclass1=val>>4; + header2ndbyte.halfbyte.tclass2=val; + + /* Write the bytes back to the header */ + h.ip6_start[0]=header1stbyte.fullbyte; + h.ip6_start[1]=header2ndbyte.fullbyte; + + return OP_SUCCESS; +} /* End of setTrafficClass() */ + + +u8 IPv6Header::getTrafficClass() const { + union{ + struct firstbyte{ + #if WORDS_BIGENDIAN + u8 ver:4; + u8 tclass1:4; + #else + u8 tclass1:4; + u8 ver:4; + #endif + }halfbyte; + u8 fullbyte; + }header1stbyte; + union{ + struct firstbyte{ + #if WORDS_BIGENDIAN + u8 tclass2:4; + u8 flow:4; + #else + u8 flow:4; + u8 tclass2:4; + #endif + }halfbyte; + u8 fullbyte; + }header2ndbyte; + union{ + struct firstbyte{ + #if WORDS_BIGENDIAN + u8 tclass1:4; + u8 tclass2:4; + #else + u8 tclass2:4; + u8 tclass1:4; + #endif + }halfbyte; + u8 fullbyte; + }finalbyte; + + header1stbyte.fullbyte = h.ip6_start[0]; + header2ndbyte.fullbyte = h.ip6_start[1]; + finalbyte.halfbyte.tclass1=header1stbyte.halfbyte.tclass1; + finalbyte.halfbyte.tclass2=header2ndbyte.halfbyte.tclass2; + return finalbyte.fullbyte; +} /* End of getTrafficClass() */ + + +int IPv6Header::setFlowLabel(u32 val){ + u32 netbyte = htonl(val); + u8 *pnt=(u8*)&netbyte; + union{ + struct firstbyte{ + #if WORDS_BIGENDIAN + u8 tclass2:4; + u8 flow:4; + #else + u8 flow:4; + u8 tclass2:4; + #endif + }halfbyte; + u8 fullbyte; + }header2ndbyte; + + header2ndbyte.fullbyte = h.ip6_start[1]; + header2ndbyte.halfbyte.flow=pnt[1]; + h.ip6_start[1]=header2ndbyte.fullbyte; + h.ip6_start[2]=pnt[2]; + h.ip6_start[3]=pnt[3]; + return OP_SUCCESS; +} /* End of setFlowLabel() */ + + +u32 IPv6Header::getFlowLabel() const { + u32 hostbyte=0; + u8 *pnt=(u8*)&hostbyte; + union{ + struct firstbyte{ + #if WORDS_BIGENDIAN + u8 tclass2:4; + u8 flow:4; + #else + u8 flow:4; + u8 tclass2:4; + #endif + }halfbyte; + u8 fullbyte; + }header2ndbyte; + + header2ndbyte.fullbyte = h.ip6_start[1]; + pnt[0]=0; + pnt[1]=header2ndbyte.halfbyte.flow; + pnt[2]=h.ip6_start[2]; + pnt[3]=h.ip6_start[3]; + hostbyte=ntohl(hostbyte); + return hostbyte; +} /* End of getFlowLabel() */ + + +int IPv6Header::setPayloadLength(u16 val){ + this->h.ip6_len = htons(val); + return OP_SUCCESS; +} /* End of setPayloadLength() */ + + +int IPv6Header::setPayloadLength(){ + int otherslen=0; + if (next!=NULL) + otherslen=next->getLen(); + setPayloadLength( otherslen ); + return OP_SUCCESS; +} /* End of setTotalLength() */ + + +u16 IPv6Header::getPayloadLength() const { + return ntohs(this->h.ip6_len); +} /* End of getPayloadLength() */ + + +int IPv6Header::setNextHeader(u8 val){ + this->h.ip6_nh = val; + return OP_SUCCESS; +} /* End of setNextHeader() */ + + +u8 IPv6Header::getNextHeader() const { + return this->h.ip6_nh; +} /* End of getNextHeader() */ + + +/** Sets field "next header" to the number that corresponds to the supplied + * protocol name. Currently only TCP, UDP and ICMP are supported. Any + * help to extend this functionality would be appreciated. For a list of all + * proto names and numbers check: + * http://www.iana.org/assignments/protocol-numbers/ */ +int IPv6Header::setNextHeader(const char *p){ + + if (p==NULL){ + printf("setNextProto(): NULL pointer supplied\n"); + return OP_FAILURE; + } + if( !strcasecmp(p, "TCP") ) + setNextHeader(6); /* 6=IANA number for proto TCP */ + else if( !strcasecmp(p, "UDP") ) + setNextHeader(17); /* 17=IANA number for proto UDP */ + else if( !strcasecmp(p, "ICMPv6")) + setNextHeader(58); /* 58=IANA number for proto ICMPv6 */ + else + netutil_fatal("setNextProto(): Invalid protocol number\n"); + return OP_SUCCESS; +} /* End of setNextHeader() */ + + +int IPv6Header::setHopLimit(u8 val){ + this->h.ip6_hopl = val; + return OP_SUCCESS; +} /* End of setHopLimit() */ + + +u8 IPv6Header::getHopLimit() const { + return this->h.ip6_hopl; +} /* End of getHopLimit() */ + + +int IPv6Header::setSourceAddress(u8 *val){ + if(val==NULL) + netutil_fatal("setSourceAddress(): NULL value supplied."); + memcpy(this->h.ip6_src, val, 16); + return OP_SUCCESS; +} /* End of setSourceAddress() */ + + +int IPv6Header::setSourceAddress(struct in6_addr val){ + memcpy(this->h.ip6_src, val.s6_addr, 16); + return OP_SUCCESS; +} /* End of setSourceAddress() */ + + +const u8 *IPv6Header::getSourceAddress() const { + return this->h.ip6_src; +} /* End of getSourceAddress() */ + + +/** Returns source IPv6 address + * @warning Returned value is in NETWORK byte order. */ +struct in6_addr IPv6Header::getSourceAddress(struct in6_addr *result) const { + struct in6_addr myaddr; + memset(&myaddr, 0, sizeof(myaddr)); + memcpy(myaddr.s6_addr, this->h.ip6_src, 16); + + if(result!=NULL) + *result=myaddr; + return myaddr; +} /* End of getSourceAddress() */ + + +int IPv6Header::setDestinationAddress(u8 *val){ + if(val==NULL) + netutil_fatal("setDestinationAddress(): NULL value supplied."); + memcpy(this->h.ip6_dst, val, 16); + return OP_SUCCESS; +} /* End of setDestinationAddress() */ + + +int IPv6Header::setDestinationAddress(struct in6_addr val){ + memcpy(this->h.ip6_dst, val.s6_addr, 16); + return OP_SUCCESS; +} /* End of setDestinationAddress() */ + + +/** Returns destination IPv6 address. */ +const u8 *IPv6Header::getDestinationAddress() const { + return this->h.ip6_dst; +} /* End of getDestinationAddress() */ + + +/** Returns destination IPv6 address + * @warning Returned value is in NETWORK byte order. */ +struct in6_addr IPv6Header::getDestinationAddress(struct in6_addr *result) const { + struct in6_addr myaddr; + memset(&myaddr, 0, sizeof(myaddr)); + memcpy(myaddr.s6_addr, this->h.ip6_dst, 16); + + if(result!=NULL) + *result=myaddr; + return myaddr; +} /* End of getDestinationAddress() */ + + +/** Returns the length of an IPv4 address. */ +u16 IPv6Header::getAddressLength() const { + return 16; +} /* End of getAddressLength()*/ + diff --git a/libnetutil/IPv6Header.h b/libnetutil/IPv6Header.h new file mode 100644 index 0000000..4932923 --- /dev/null +++ b/libnetutil/IPv6Header.h @@ -0,0 +1,172 @@ +/*************************************************************************** + * IPv6Header.h -- The IPv6Header Class represents an IPv6 datagram. It * + * contains methods to set any header field. In general, these methods do * + * error checkings and byte order conversion. * + * * + ***********************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. */ + +#ifndef IPV6HEADER_H +#define IPV6HEADER_H 1 + +#include "NetworkLayerElement.h" + +#define IPv6_HEADER_LEN 40 + +/* Default header values */ +#define IPv6_DEFAULT_TCLASS 0 +#define IPv6_DEFAULT_FLABEL 0 +#define IPv6_DEFAULT_HOPLIM 64 +#define IPv6_DEFAULT_NXTHDR 6 /* TCP */ + +class IPv6Header : public NetworkLayerElement { + + private: + + /* IPv6 Header Format: + 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 2 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |Version| Traffic Class | Flow Label | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Payload Length | Next Header | Hop Limit | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + +-- --+ + | | + +-- Source Address --+ + | | + +-- --+ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + +-- --+ + | | + +-- Destination Address --+ + | | + +-- --+ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + struct nping_ipv6_hdr { + u8 ip6_start[4]; /* Version, Traffic and Flow */ + u16 ip6_len; /* Payload length */ + u8 ip6_nh; /* Next Header */ + u8 ip6_hopl; /* Hop Limit */ + u8 ip6_src[16]; /* Source IP Address */ + u8 ip6_dst[16]; /* Destination IP Address */ + }__attribute__((__packed__)); + + typedef struct nping_ipv6_hdr nping_ipv6_hdr_t; + + nping_ipv6_hdr_t h; + + public: + + /* Misc */ + IPv6Header(); + ~IPv6Header(); + void reset(); + u8 *getBufferPointer(); + int storeRecvData(const u8 *buf, size_t len); + int protocol_id() const; + int validate(); + int print(FILE *output, int detail) const; + + /* IP version */ + int setVersion(); + int setVersion(u8 val); + u8 getVersion() const; + + /* Traffic class */ + int setTrafficClass(u8 val); + u8 getTrafficClass() const; + + /* Flow Label */ + int setFlowLabel(u32 val); + u32 getFlowLabel() const; + + /* Payload Length */ + int setPayloadLength(u16 val); + int setPayloadLength(); + u16 getPayloadLength() const; + + /* Next Header */ + int setNextHeader(u8 val); + int setNextHeader(const char *p); + u8 getNextHeader() const; + + /* Hop Limit */ + int setHopLimit(u8 val); + u8 getHopLimit() const; + + /* Source Address */ + int setSourceAddress(u8 *val); + int setSourceAddress(struct in6_addr val); + const u8 *getSourceAddress() const; + struct in6_addr getSourceAddress(struct in6_addr *result) const; + + /* Destination Address*/ + int setDestinationAddress(u8 *val); + int setDestinationAddress(struct in6_addr val); + const u8 *getDestinationAddress() const; + struct in6_addr getDestinationAddress(struct in6_addr *result) const; + + u16 getAddressLength() const; +}; + +#endif diff --git a/libnetutil/Makefile.in b/libnetutil/Makefile.in new file mode 100644 index 0000000..c7deb92 --- /dev/null +++ b/libnetutil/Makefile.in @@ -0,0 +1,41 @@ +top_srcdir = @top_srcdir@ +srcdir = @srcdir@ + +CXX = @CXX@ +CXXFLAGS = @CXXFLAGS@ +CPPFLAGS = @CPPFLAGS@ $(DEFS) +DEFS = @DEFS@ +DEFS += -D_FORTIFY_SOURCE=2 +AR = ar +RANLIB = @RANLIB@ + +LIBDNETDIR = @LIBDNETDIR@ +LIBPCAPDIR = @libpcapdir@ + +TARGET = libnetutil.a + +SRCS = $(srcdir)/netutil.cc $(srcdir)/PacketElement.cc $(srcdir)/NetworkLayerElement.cc $(srcdir)/ARPHeader.cc $(srcdir)/PacketElement.cc $(srcdir)/NetworkLayerElement.cc $(srcdir)/TransportLayerElement.cc $(srcdir)/ARPHeader.cc $(srcdir)/EthernetHeader.cc $(srcdir)/ICMPv4Header.cc $(srcdir)/ICMPv6Header.cc $(srcdir)/IPv4Header.cc $(srcdir)/IPv6Header.cc $(srcdir)/TCPHeader.cc $(srcdir)/UDPHeader.cc $(srcdir)/RawData.cc $(srcdir)/HopByHopHeader.cc $(srcdir)/DestOptsHeader.cc $(srcdir)/FragmentHeader.cc $(srcdir)/RoutingHeader.cc $(srcdir)/PacketParser.cc +OBJS = netutil.o PacketElement.o NetworkLayerElement.o TransportLayerElement.o ARPHeader.o EthernetHeader.o ICMPv4Header.o ICMPv6Header.o IPv4Header.o IPv6Header.o TCPHeader.o UDPHeader.o RawData.o HopByHopHeader.o DestOptsHeader.o FragmentHeader.o RoutingHeader.o PacketParser.o + +all: $(TARGET) + +$(TARGET): $(OBJS) + rm -f $@ + $(AR) cr $@ $(OBJS) + $(RANLIB) $@ + +clean: + rm -f $(OBJS) $(TARGET) + +distclean: clean + rm -rf Makefile makefile.dep + +Makefile: Makefile.in + cd $(top_srcdir) && ./config.status + +.cc.o: + $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@ + +makefile.dep: + $(CXX) -MM $(CPPFLAGS) $(SRCS) > $@ +-include makefile.dep diff --git a/libnetutil/NetworkLayerElement.cc b/libnetutil/NetworkLayerElement.cc new file mode 100644 index 0000000..6a85489 --- /dev/null +++ b/libnetutil/NetworkLayerElement.cc @@ -0,0 +1,62 @@ +/*************************************************************************** + * NetworkLayerElement.cc -- Class NetworkLayerElement is a generic class * + * that represents a network layer protocol header. Classes like IPv4Header* + * or IPv6Header inherit from it. * + * * + ***********************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 "NetworkLayerElement.h" diff --git a/libnetutil/NetworkLayerElement.h b/libnetutil/NetworkLayerElement.h new file mode 100644 index 0000000..eb185ad --- /dev/null +++ b/libnetutil/NetworkLayerElement.h @@ -0,0 +1,92 @@ +/*************************************************************************** + * NetworkLayerElement.h -- Class NetworkLayerElement is a generic class * + * that represents a network layer protocol header. Classes like IPv4Header* + * or IPv6Header inherit from it. * + * * + ***********************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. */ + +#ifndef NETWORKLAYERELEMENT_H +#define NETWORKLAYERELEMENT_H 1 + +#include "PacketElement.h" + +/// class NetworkLayerElement - +class NetworkLayerElement : public PacketElement { + + public: + virtual u16 getAddressLength() const{ + return 0; + } + + virtual const u8 *getSourceAddress() const{ + return NULL; + } + + virtual const u8 *getDestinationAddress() const{ + return NULL; + } + + virtual int setNextHeader(u8 val){ + return 0; + } + + virtual u8 getNextHeader() const{ + return 0; + } +}; + +#endif diff --git a/libnetutil/PacketElement.cc b/libnetutil/PacketElement.cc new file mode 100644 index 0000000..61c9b74 --- /dev/null +++ b/libnetutil/PacketElement.cc @@ -0,0 +1,70 @@ +/*************************************************************************** + * PacketElement.cc -- The PacketElement Class is a generic class that * + * represents a protocol header or a part of a network packet. Many other * + * classes inherit from it (NetworkLayerElement, TransportLayerElement, * + * etc). * + * * + ***********************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 "PacketElement.h" + +PacketElement::PacketElement(){ + next=NULL; /* It's very important these get initialized to NULL */ + prev=NULL; + length=0; +} /* End of PacketElement constructor */ + diff --git a/libnetutil/PacketElement.h b/libnetutil/PacketElement.h new file mode 100644 index 0000000..834956c --- /dev/null +++ b/libnetutil/PacketElement.h @@ -0,0 +1,268 @@ +/*************************************************************************** + * PacketElement.h -- The PacketElement Class is a generic class that * + * represents a protocol header or a part of a network packet. Many other * + * classes inherit from it (NetworkLayerElement, TransportLayerElement, * + * etc). * + * * + ***********************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. */ + +#ifndef PACKETELEMENT_H +#define PACKETELEMENT_H 1 + +#include "nbase.h" +#include "netutil.h" + +#define HEADER_TYPE_IPv6_HOPOPT 0 /* IPv6 Hop-by-Hop Option */ +#define HEADER_TYPE_ICMPv4 1 /* ICMP Internet Control Message */ +#define HEADER_TYPE_IGMP 2 /* IGMP Internet Group Management */ +#define HEADER_TYPE_IPv4 4 /* IPv4 IPv4 encapsulation */ +#define HEADER_TYPE_TCP 6 /* TCP Transmission Control */ +#define HEADER_TYPE_EGP 8 /* EGP Exterior Gateway Protocol */ +#define HEADER_TYPE_UDP 17 /* UDP User Datagram */ +#define HEADER_TYPE_IPv6 41 /* IPv6 IPv6 encapsulation */ +#define HEADER_TYPE_IPv6_ROUTE 43 /* IPv6-Route Routing Header for IPv6 */ +#define HEADER_TYPE_IPv6_FRAG 44 /* IPv6-Frag Fragment Header for IPv6 */ +#define HEADER_TYPE_GRE 47 /* GRE General Routing Encapsulation */ +#define HEADER_TYPE_ESP 50 /* ESP Encap Security Payload */ +#define HEADER_TYPE_AH 51 /* AH Authentication Header */ +#define HEADER_TYPE_ICMPv6 58 /* IPv6-ICMP ICMP for IPv6 */ +#define HEADER_TYPE_IPv6_NONXT 59 /* IPv6-NoNxt No Next Header for IPv6 */ +#define HEADER_TYPE_IPv6_OPTS 60 /* IPv6-Opts IPv6 Destination Options */ +#define HEADER_TYPE_EIGRP 88 /* EIGRP */ +#define HEADER_TYPE_ETHERNET 97 /* Ethernet */ +#define HEADER_TYPE_L2TP 115 /* L2TP Layer Two Tunneling Protocol */ +#define HEADER_TYPE_SCTP 132 /* SCTP Stream Control Transmission P. */ +#define HEADER_TYPE_IPv6_MOBILE 135 /* Mobility Header */ +#define HEADER_TYPE_MPLS_IN_IP 137 /* MPLS-in-IP */ +#define HEADER_TYPE_ARP 2054 /* ARP Address Resolution Protocol */ +#define HEADER_TYPE_ICMPv6_OPTION 9997 /* ICMPv6 option */ +#define HEADER_TYPE_NEP 9998 /* Nping Echo Protocol */ +#define HEADER_TYPE_RAW_DATA 9999 /* Raw unknown data */ + +#define PRINT_DETAIL_LOW 1 +#define PRINT_DETAIL_MED 2 +#define PRINT_DETAIL_HIGH 3 + +#define DEFAULT_PRINT_DETAIL (PRINT_DETAIL_LOW) +#define DEFAULT_PRINT_DESCRIPTOR stdout + +class PacketElement { + + protected: + + int length; + PacketElement *next; /**< Next PacketElement (next proto header) */ + PacketElement *prev; /**< Prev PacketElement (previous proto header) */ + + public: + + PacketElement(); + + virtual ~PacketElement(){ + + } /* End of PacketElement destructor */ + + /** This function MUST be overwritten on ANY class that inherits from + * this one. Otherwise getBinaryBuffer will fail */ + virtual u8 * getBufferPointer(){ + netutil_fatal("getBufferPointer(): Attempting to use superclass PacketElement method.\n"); + return NULL; + } /* End of getBufferPointer() */ + + + /** Returns a buffer that contains the header of the packet + all the + * lower level headers and payload. Returned buffer should be ok to be + * passes to a send() call to be transferred trough a socket. + * @return a pointer to a free()able buffer that contains packet's binary + * data. + * @warning If there are linked elements, their getBinaryBuffer() method + * will be called recursively and the buffers that they return WILL be + * free()d as soon as we copy the data in our own allocated buffer. + * @warning Calls to this method may not ve very efficient since they + * always involved a few malloc()s and free()s. If you want efficiency + * use dumpToBinaryBuffer(); */ + virtual u8 * getBinaryBuffer(){ + u8 *ourbuff=NULL; + u8 *othersbuff=NULL; + u8 *totalbuff=NULL; + long otherslen=0; + + /* Get our own buffer address */ + if ( (ourbuff=getBufferPointer()) == NULL ){ + netutil_fatal("getBinaryBuffer(): Couldn't get own data pointer\n"); + } + if( next != NULL ){ /* There is some other packet element */ + othersbuff = next->getBinaryBuffer(); + otherslen=next->getLen(); + totalbuff=(u8 *)safe_zalloc(otherslen + length); + memcpy(totalbuff, ourbuff, length); + memcpy(totalbuff+length, othersbuff, otherslen); + free(othersbuff); + }else{ + totalbuff=(u8 *)safe_zalloc(length); + memcpy(totalbuff, ourbuff, length); + } + return totalbuff; + } /* End of getBinaryBuffer() */ + + + virtual int dumpToBinaryBuffer(u8* dst, int maxlen){ + u8 *ourbuff=NULL; + long ourlength=0; + /* Get our own buffer address and length */ + if ( (ourbuff=getBufferPointer()) == NULL || (ourlength=this->length) < 0 ) + netutil_fatal("getBinaryBuffer(): Couldn't get own data pointer\n"); + /* Copy our part of the buffer */ + if ( maxlen < ourlength ) + netutil_fatal("getBinaryBuffer(): Packet exceeds maximum length %d\n", maxlen); + memcpy( dst, ourbuff, ourlength); + /* If there are more elements, tell them to copy their part */ + if( next!= NULL ){ + next->dumpToBinaryBuffer(dst+ourlength, maxlen-ourlength); + } + return this->getLen(); + } /* End of dumpToBinaryBuffer() */ + + + /** Does the same as the previous one but it stores the length of the + * return buffer on the memory pointed by the supplied int pointer. */ + virtual u8 * getBinaryBuffer(int *len){ + u8 *buff = getBinaryBuffer(); + if( len != NULL ) + *len = getLen(); + return buff; + } /* End of getBinaryBuffer() */ + + + /** Returns the length of this PacketElement + the length of all the + * PacketElements that are next to it (are linked trough the "next" + * attribute). So for example, if we have IPv4Header p1, linked to + * a TCPHeader p2, representing a simple TCP SYN with no options, + * a call to p1.getLen() will return 20 (IP header with no options) + 20 + * (TCP header with no options) = 40 bytes. */ + int getLen() const { + /* If we have some other packet element linked, get its length */ + if (next!=NULL) + return length + next->getLen(); + else + return length; + } /* End of getLen() */ + + + /** Returns the address of the next PacketElement that is linked to this */ + virtual PacketElement *getNextElement() const { + return next; + } /* End of getNextElement() */ + + + /** Links current object with the next header in the protocol chain. Note + * that this method also links the next element with this one, calling + * setPrevElement(). */ + virtual int setNextElement(PacketElement *n){ + next=n; + if(next!=NULL) + next->setPrevElement(this); + return OP_SUCCESS; + } /* End of setNextElement() */ + + /** Sets attribute prev with the supplied pointer value. + * @warning Supplied pointer must point to a PacketElement object or + * an object that inherits from it. */ + virtual int setPrevElement(PacketElement *n){ + this->prev=n; + return OP_SUCCESS; + } /* End of setPrevElement() */ + + + /** Returns the address of the previous PacketElement that is linked to + * this one. + * @warning In many cases this function will return NULL since there is + * a high probability that the user of this class does not link + * PacketElements in both directions. Normally one would set attribute + * "next" of an IPHeader object to the TCPHeader that follows it, but + * not the other way around. */ + virtual PacketElement *getPrevElement(){ + return prev; + } /* End of getPrevElement() */ + + /** This method should be overwritten by any class that inherits from + * PacketElement. It should print the object contents and then call + * this->next->print(), providing this->next!=NULL */ + virtual int print(FILE *output, int detail) const { + if(this->next!=NULL) + this->next->print(output, detail); + return OP_SUCCESS; + } /* End of printf() */ + + virtual int print() const { + return print(DEFAULT_PRINT_DESCRIPTOR, DEFAULT_PRINT_DETAIL); + } + + virtual int print(int detail) const { + return print(DEFAULT_PRINT_DESCRIPTOR, detail); + } + + virtual void print_separator(FILE *output, int detail) const { + fprintf(output, " "); + } + + /* Returns the type of protocol an object represents. This method MUST + * be overwritten by all children. */ + virtual int protocol_id() const = 0; +}; + +#endif diff --git a/libnetutil/PacketParser.cc b/libnetutil/PacketParser.cc new file mode 100644 index 0000000..01c661b --- /dev/null +++ b/libnetutil/PacketParser.cc @@ -0,0 +1,1789 @@ +/*************************************************************************** + * PacketParser.cc -- The PacketParser Class offers methods to parse * + * received network packets. Its main purpose is to facilitate the * + * conversion of raw sequences of bytes into chains of objects of the * + * PacketElement family. * + * * + ***********************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 "PacketParser.h" +#include <assert.h> + +#define PKTPARSERDEBUG false + +PacketParser::PacketParser() { + this->reset(); +} /* End of PacketParser constructor */ + + +PacketParser::~PacketParser() { + +} /* End of PacketParser destructor */ + + +/** Sets every attribute to its default value- */ +void PacketParser::reset() { + +} /* End of PacketParser destructor */ + + +const char *PacketParser::header_type2string(int val){ + header_type_string_t header_types[]={ + {HEADER_TYPE_IPv6_HOPOPT, "IPv6 Hop-by-Hop"}, + {HEADER_TYPE_ICMPv4,"ICMPv4"}, + {HEADER_TYPE_IGMP,"IGMP"}, + {HEADER_TYPE_IPv4,"IPv4"}, + {HEADER_TYPE_TCP,"TCP"}, + {HEADER_TYPE_EGP,"EGP"}, + {HEADER_TYPE_UDP,"UDP"}, + {HEADER_TYPE_IPv6,"IPv6"}, + {HEADER_TYPE_IPv6_ROUTE,"IPv6-Route"}, + {HEADER_TYPE_IPv6_FRAG,"IPv6-Frag"}, + {HEADER_TYPE_GRE,"GRE"}, + {HEADER_TYPE_ESP,"ESP"}, + {HEADER_TYPE_AH,"AH"}, + {HEADER_TYPE_ICMPv6,"ICMPv6"}, + {HEADER_TYPE_IPv6_NONXT,"IPv6-NoNxt"}, + {HEADER_TYPE_IPv6_OPTS,"IPv6-Opts"}, + {HEADER_TYPE_EIGRP,"EIGRP"}, + {HEADER_TYPE_ETHERNET,"Ethernet"}, + {HEADER_TYPE_L2TP,"L2TP"}, + {HEADER_TYPE_SCTP,"SCTP"}, + {HEADER_TYPE_IPv6_MOBILE,"Mobility Header"}, + {HEADER_TYPE_MPLS_IN_IP,"MPLS-in-IP"}, + {HEADER_TYPE_ARP,"ARP"}, + {HEADER_TYPE_RAW_DATA,"Raw Data"}, + {0,NULL} + }; + int i=0; + for(i=0; header_types[i].str!=NULL; i++ ){ + if((int)header_types[i].type==val) + return header_types[i].str; + } + return NULL; +} /* End of header_type2string() */ + + + +#define MAX_HEADERS_IN_PACKET 32 +pkt_type_t *PacketParser::parse_packet(const u8 *pkt, size_t pktlen, bool eth_included){ + if(PKTPARSERDEBUG)printf("%s(%p, %lu)\n", __func__, pkt, (long unsigned)pktlen); + static pkt_type_t this_packet[MAX_HEADERS_IN_PACKET+1]; /* Packet structure array */ + u8 current_header=0; /* Current array position of "this_packet" */ + const u8 *curr_pkt=pkt; /* Pointer to current part of the packet */ + size_t curr_pktlen=pktlen; /* Remaining packet length */ + int ethlen=0, arplen=0; /* Aux length variables: link layer */ + int iplen=0,ip6len=0; /* Aux length variables: network layer */ + int tcplen=0,udplen=0,icmplen=0; /* Aux length variables: transport layer */ + int exthdrlen=0; /* Aux length variables: extension headers */ + int next_layer=0; /* Next header type to process */ + int expected=0; /* Next protocol expected */ + bool finished=false; /* Loop breaking flag */ + bool unknown_hdr=false; /* Indicates unknown header found */ + IPv4Header ip4; + IPv6Header ip6; + TCPHeader tcp; + UDPHeader udp; + ICMPv4Header icmp4; + ICMPv6Header icmp6; + EthernetHeader eth; + DestOptsHeader ext_dopts; + FragmentHeader ext_frag; + HopByHopHeader ext_hopt; + RoutingHeader ext_routing; + ARPHeader arp; + memset(this_packet, 0, sizeof(this_packet)); + + /* Decide which layer we have to start from */ + if( eth_included ){ + next_layer=LINK_LAYER; + expected=HEADER_TYPE_ETHERNET; + }else{ + next_layer=NETWORK_LAYER; + } + + /* Header processing loop */ + while(!finished && curr_pktlen>0 && current_header<MAX_HEADERS_IN_PACKET){ + /* Ethernet and ARP headers ***********************************************/ + if(next_layer==LINK_LAYER ){ + if(PKTPARSERDEBUG)puts("Next Layer=Link"); + if(expected==HEADER_TYPE_ETHERNET){ + if(PKTPARSERDEBUG)puts("Expected Layer=Ethernet"); + if(eth.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE){ + unknown_hdr=true; + break; + } + if( (ethlen=eth.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + /* Determine next header type */ + switch( eth.getEtherType() ){ + case ETHTYPE_IPV4: + expected=HEADER_TYPE_IPv4; + next_layer=NETWORK_LAYER; + break; + case ETHTYPE_IPV6: + expected=HEADER_TYPE_IPv6; + next_layer=NETWORK_LAYER; + break; + case ETHTYPE_ARP: + next_layer=LINK_LAYER; + expected=HEADER_TYPE_ARP; + break; + default: + next_layer=APPLICATION_LAYER; + expected=HEADER_TYPE_RAW_DATA; + break; + } + this_packet[current_header].length=ethlen; + this_packet[current_header++].type=HEADER_TYPE_ETHERNET; + eth.reset(); + curr_pkt+=ethlen; + curr_pktlen-=ethlen; + }else if(expected==HEADER_TYPE_ARP){ + if(PKTPARSERDEBUG)puts("Expected Layer=ARP"); + if(arp.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE){ + unknown_hdr=true; + break; + } + if( (arplen=arp.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + this_packet[current_header].length=arplen; + this_packet[current_header++].type=HEADER_TYPE_ARP; + arp.reset(); + curr_pkt+=arplen; + curr_pktlen-=arplen; + if(curr_pktlen>0){ + next_layer=APPLICATION_LAYER; + expected=HEADER_TYPE_RAW_DATA; + }else{ + finished=true; + } + }else{ + assert(finished==true); + } + /* IPv4 and IPv6 headers **************************************************/ + }else if(next_layer==NETWORK_LAYER){ + if(PKTPARSERDEBUG)puts("Next Layer=Network"); + /* Determine IP version */ + if (ip4.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE){ + unknown_hdr=true; + break; + } + + /* IP version 4 ---------------------------------*/ + if(ip4.getVersion()==4){ + if( (iplen=ip4.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + /* Determine next header type */ + switch(ip4.getNextProto()){ + case HEADER_TYPE_ICMPv4: + next_layer=TRANSPORT_LAYER; + expected=HEADER_TYPE_ICMPv4; + break; + case HEADER_TYPE_IPv4: /* IP in IP */ + next_layer=NETWORK_LAYER; + expected=HEADER_TYPE_IPv4; + break; + case HEADER_TYPE_TCP: + next_layer=TRANSPORT_LAYER; + expected=HEADER_TYPE_TCP; + break; + case HEADER_TYPE_UDP: + next_layer=TRANSPORT_LAYER; + expected=HEADER_TYPE_UDP; + break; + case HEADER_TYPE_IPv6: /* IPv6 in IPv4 */ + next_layer=NETWORK_LAYER; + expected=HEADER_TYPE_IPv6; + break; + default: + next_layer=APPLICATION_LAYER; + expected=HEADER_TYPE_RAW_DATA; + break; + } + this_packet[current_header].length=iplen; + this_packet[current_header++].type=HEADER_TYPE_IPv4; + ip4.reset(); + curr_pkt+=iplen; + curr_pktlen-=iplen; + /* IP version 6 ---------------------------------*/ + }else if(ip4.getVersion()==6){ + ip4.reset(); + if (ip6.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE){ + unknown_hdr=true; + break; + } + if( (ip6len=ip6.validate())==OP_FAILURE ){ + unknown_hdr=true; + break; + } + switch( ip6.getNextHeader() ){ + case HEADER_TYPE_ICMPv6: + next_layer=TRANSPORT_LAYER; + expected=HEADER_TYPE_ICMPv6; + break; + case HEADER_TYPE_IPv4: /* IPv4 in IPv6 */ + next_layer=NETWORK_LAYER; + expected=HEADER_TYPE_IPv4; + break; + case HEADER_TYPE_TCP: + next_layer=TRANSPORT_LAYER; + expected=HEADER_TYPE_TCP; + break; + case HEADER_TYPE_UDP: + next_layer=TRANSPORT_LAYER; + expected=HEADER_TYPE_UDP; + break; + case HEADER_TYPE_IPv6: /* IPv6 in IPv6 */ + next_layer=NETWORK_LAYER; + expected=HEADER_TYPE_IPv6; + break; + case HEADER_TYPE_IPv6_HOPOPT: + next_layer=EXTHEADERS_LAYER; + expected=HEADER_TYPE_IPv6_HOPOPT; + break; + case HEADER_TYPE_IPv6_OPTS: + next_layer=EXTHEADERS_LAYER; + expected=HEADER_TYPE_IPv6_OPTS; + break; + case HEADER_TYPE_IPv6_ROUTE: + next_layer=EXTHEADERS_LAYER; + expected=HEADER_TYPE_IPv6_ROUTE; + break; + case HEADER_TYPE_IPv6_FRAG: + next_layer=EXTHEADERS_LAYER; + expected=HEADER_TYPE_IPv6_FRAG; + break; + default: + next_layer=APPLICATION_LAYER; + expected=HEADER_TYPE_RAW_DATA; + break; + } + this_packet[current_header].length=ip6len; + this_packet[current_header++].type=HEADER_TYPE_IPv6; + ip6.reset(); + curr_pkt+=ip6len; + curr_pktlen-=ip6len; + /* Bogus IP version -----------------------------*/ + }else{ + /* Wrong IP version, treat as raw data. */ + next_layer=APPLICATION_LAYER; + expected=HEADER_TYPE_RAW_DATA; + } + /* TCP, UDP, ICMPv4 and ICMPv6 headers ************************************/ + }else if(next_layer==TRANSPORT_LAYER){ + if(PKTPARSERDEBUG)puts("Next Layer=Transport"); + if(expected==HEADER_TYPE_TCP){ + if(PKTPARSERDEBUG)puts("Expected Layer=TCP"); + if(tcp.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE ){ + unknown_hdr=true; + break; + } + if( (tcplen=tcp.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + expected=HEADER_TYPE_RAW_DATA; + this_packet[current_header].length=tcplen; + this_packet[current_header++].type=HEADER_TYPE_TCP; + tcp.reset(); + curr_pkt+=tcplen; + curr_pktlen-=tcplen; + next_layer=APPLICATION_LAYER; + }else if(expected==HEADER_TYPE_UDP){ + if(PKTPARSERDEBUG)puts("Expected Layer=UDP"); + if(udp.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE ){ + unknown_hdr=true; + break; + } + if( (udplen=udp.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + expected=HEADER_TYPE_RAW_DATA; + this_packet[current_header].length=udplen; + this_packet[current_header++].type=HEADER_TYPE_UDP; + udp.reset(); + curr_pkt+=udplen; + curr_pktlen-=udplen; + next_layer=APPLICATION_LAYER; + }else if(expected==HEADER_TYPE_ICMPv4){ + if(PKTPARSERDEBUG)puts("Expected Layer=ICMPv4"); + if(icmp4.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE ){ + unknown_hdr=true; + break; + } + if( (icmplen=icmp4.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + switch( icmp4.getType() ){ + /* Types that include an IPv4 packet as payload */ + case ICMP_UNREACH: + case ICMP_TIMXCEED: + case ICMP_PARAMPROB: + case ICMP_SOURCEQUENCH: + case ICMP_REDIRECT: + next_layer=NETWORK_LAYER; + expected=HEADER_TYPE_IPv4; + break; + /* ICMP types that include misc payloads (or no payload) */ + default: + expected=HEADER_TYPE_RAW_DATA; + next_layer=APPLICATION_LAYER; + break; + } + this_packet[current_header].length=icmplen; + this_packet[current_header++].type=HEADER_TYPE_ICMPv4; + icmp4.reset(); + curr_pkt+=icmplen; + curr_pktlen-=icmplen; + }else if(expected==HEADER_TYPE_ICMPv6){ + if(PKTPARSERDEBUG)puts("Expected Layer=ICMPv6"); + if(icmp6.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE){ + unknown_hdr=true; + break; + } + if( (icmplen=icmp6.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + switch( icmp6.getType() ){ + /* Types that include an IPv6 packet as payload */ + case ICMPv6_UNREACH: + case ICMPv6_PKTTOOBIG: + case ICMPv6_TIMXCEED: + case ICMPv6_PARAMPROB: + next_layer=NETWORK_LAYER; + expected=HEADER_TYPE_IPv6; + break; + /* ICMPv6 types that include misc payloads (or no payload) */ + default: + expected=HEADER_TYPE_RAW_DATA; + next_layer=APPLICATION_LAYER; + break; + } + this_packet[current_header].length=icmplen; + this_packet[current_header++].type=HEADER_TYPE_ICMPv6; + icmp6.reset(); + curr_pkt+=icmplen; + curr_pktlen-=icmplen; + }else{ + /* Wrong application layer protocol, treat as raw data. */ + next_layer=APPLICATION_LAYER; + expected=HEADER_TYPE_RAW_DATA; + } + + /* IPv6 Extension Headers */ + }else if(next_layer==EXTHEADERS_LAYER){ + if(PKTPARSERDEBUG)puts("Next Layer=ExtHdr"); + u8 ext_next=0; + /* Hop-by-Hop Options */ + if(expected==HEADER_TYPE_IPv6_HOPOPT){ + if(PKTPARSERDEBUG)puts("Expected=Hopt"); + if(ext_hopt.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE ){ + unknown_hdr=true; + break; + } + if( (exthdrlen=ext_hopt.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + ext_next=ext_hopt.getNextHeader(); + ext_hopt.reset(); + /* Routing Header */ + }else if(expected==HEADER_TYPE_IPv6_ROUTE){ + if(PKTPARSERDEBUG)puts("Expected=Route"); + if(ext_routing.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE ){ + unknown_hdr=true; + break; + } + if( (exthdrlen=ext_routing.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + ext_next=ext_routing.getNextHeader(); + ext_routing.reset(); + /* Fragmentation Header */ + }else if(expected==HEADER_TYPE_IPv6_FRAG){ + if(PKTPARSERDEBUG)puts("Expected=Frag"); + if(ext_frag.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE ){ + unknown_hdr=true; + break; + } + if( (exthdrlen=ext_frag.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + ext_next=ext_frag.getNextHeader(); + ext_frag.reset(); + /* Destination Options Header */ + }else if(expected==HEADER_TYPE_IPv6_OPTS){ + if(PKTPARSERDEBUG)puts("Expected=Dopts"); + if(ext_dopts.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE ){ + unknown_hdr=true; + break; + } + if( (exthdrlen=ext_dopts.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + ext_next=ext_dopts.getNextHeader(); + ext_dopts.reset(); + }else{ + /* Should never happen. */ + unknown_hdr=true; + break; + } + + /* Update the info for this header */ + this_packet[current_header].length=exthdrlen; + this_packet[current_header++].type=expected; + curr_pkt+=exthdrlen; + curr_pktlen-=exthdrlen; + + /* Lets's see what comes next */ + switch(ext_next){ + case HEADER_TYPE_ICMPv6: + next_layer=TRANSPORT_LAYER; + expected=HEADER_TYPE_ICMPv6; + break; + case HEADER_TYPE_IPv4: /* IPv4 in IPv6 */ + next_layer=NETWORK_LAYER; + expected=HEADER_TYPE_IPv4; + break; + case HEADER_TYPE_TCP: + next_layer=TRANSPORT_LAYER; + expected=HEADER_TYPE_TCP; + break; + case HEADER_TYPE_UDP: + next_layer=TRANSPORT_LAYER; + expected=HEADER_TYPE_UDP; + break; + case HEADER_TYPE_IPv6: /* IPv6 in IPv6 */ + next_layer=NETWORK_LAYER; + expected=HEADER_TYPE_IPv6; + break; + case HEADER_TYPE_IPv6_HOPOPT: + next_layer=EXTHEADERS_LAYER; + expected=HEADER_TYPE_IPv6_HOPOPT; + break; + case HEADER_TYPE_IPv6_OPTS: + next_layer=EXTHEADERS_LAYER; + expected=HEADER_TYPE_IPv6_OPTS; + break; + case HEADER_TYPE_IPv6_ROUTE: + next_layer=EXTHEADERS_LAYER; + expected=HEADER_TYPE_IPv6_ROUTE; + break; + case HEADER_TYPE_IPv6_FRAG: + next_layer=EXTHEADERS_LAYER; + expected=HEADER_TYPE_IPv6_FRAG; + break; + default: + next_layer=APPLICATION_LAYER; + expected=HEADER_TYPE_RAW_DATA; + break; + } + + /* Miscellaneous payloads *************************************************/ + }else{ // next_layer==APPLICATION_LAYER + if(PKTPARSERDEBUG)puts("Next Layer=Application"); + + /* If we get here it is possible that the packet is ARP but + * we have no access to the original Ethernet header. We + * determine if this header is ARP by checking its size + * and checking for some common values. */ + if(arp.storeRecvData(curr_pkt, curr_pktlen)!=OP_FAILURE){ + if( (arplen=arp.validate())!=OP_FAILURE){ + if(arp.getHardwareType()==HDR_ETH10MB){ + if(arp.getProtocolType()==0x0800){ + if(arp.getHwAddrLen()==ETH_ADDRESS_LEN){ + if(arp.getProtoAddrLen()==IPv4_ADDRESS_LEN){ + this_packet[current_header].length=arplen; + this_packet[current_header++].type=HEADER_TYPE_ARP; + arp.reset(); + curr_pkt+=arplen; + curr_pktlen-=arplen; + if(curr_pktlen>0){ + next_layer=APPLICATION_LAYER; + expected=HEADER_TYPE_RAW_DATA; + }else{ + finished=true; + } + } + } + } + } + } + } + + //if(expected==HEADER_TYPE_DNS){ + //}else if(expected==HEADER_TYPE_HTTP){ + //}... ETC + this_packet[current_header].length=curr_pktlen; + this_packet[current_header++].type=HEADER_TYPE_RAW_DATA; + curr_pktlen=0; + finished=true; + } + } /* End of header processing loop */ + + /* If we couldn't validate some header, treat that header and any remaining + * data, as raw application data. */ + if (unknown_hdr==true){ + if(curr_pktlen>0){ + if(PKTPARSERDEBUG)puts("Unknown layer found. Treating it as raw data."); + this_packet[current_header].length=curr_pktlen; + this_packet[current_header++].type=HEADER_TYPE_RAW_DATA; + } + } + + return this_packet; +} /* End of parse_received_packet() */ + + +/* TODO: remove */ +int PacketParser::dummy_print_packet_type(const u8 *pkt, size_t pktlen, bool eth_included){ + pkt_type_t *packetheaders=PacketParser::parse_packet(pkt, pktlen, eth_included); + for(int i=0; packetheaders[i].length!=0; i++){ + printf("%s:", header_type2string(packetheaders[i].type)); + } + printf("\n"); + return OP_SUCCESS; +} /* End of dummy_print_packet_type() */ + + +int PacketParser::dummy_print_packet(const u8 *pkt, size_t pktlen, bool eth_included){ + PacketElement *me=NULL, *aux=NULL; + if( (me=split(pkt, pktlen, eth_included))==NULL ) + return OP_FAILURE; + else{ + me->print(stdout, PRINT_DETAIL_HIGH); + printf("\n"); + } + /* Free the structs */ + while(me!=NULL){ + aux=me->getNextElement(); + delete me; + me=aux; + } + return OP_SUCCESS; +} /* End of dummy_print_packet() */ + + + +/** For a given packet, this method determines where the application layer data + * begins. It returs a positive offset if any application data was found, zero + * if the packet did not contain application data and a negative integer in + * case of error. */ +int PacketParser::payload_offset(const u8 *pkt, size_t pktlen, bool link_included){ + PacketElement *me=NULL, *aux=NULL; + size_t offset=pktlen; /* Initially, point to the end of the packet. */ + + /* Safe checks*/ + if(pkt==NULL || pktlen<=0) + return -1; + + dummy_print_packet_type(pkt, pktlen, link_included); + + /* Split the packet into separate protocol headers */ + if( (me=split(pkt, pktlen, link_included))==NULL ) + return -2; + else{ + aux=me; + } + + /* Find if there is application data and where it begins */ + while(me!=NULL){ + /* When we find application data, we compute the offset by substacting the + length of the application data from the packet's total length */ + if(me->protocol_id()==HEADER_TYPE_RAW_DATA){ + offset = pktlen - me->getLen(); + break; + } + me = me->getNextElement(); + } + + /* Free the structs */ + me=aux; + while(me!=NULL){ + aux=me->getNextElement(); + delete me; + me=aux; + } + + /* Return 0 if we didn't find any application data */ + if(offset==pktlen){ + return 0; + }else{ + return offset; + } +} /* End of payload_offset() */ + + + + +PacketElement *PacketParser::split(const u8 *pkt, size_t pktlen){ + return split(pkt, pktlen, false); +} /* End of split() */ + + +PacketElement *PacketParser::split(const u8 *pkt, size_t pktlen, bool eth_included){ + pkt_type_t *packetheaders=NULL; + const u8 *curr_pkt=pkt; + PacketElement *first=NULL; + PacketElement *last=NULL; + IPv4Header *ip4=NULL; + IPv6Header *ip6=NULL; + DestOptsHeader *ext_dopts=NULL; + FragmentHeader *ext_frag=NULL; + HopByHopHeader *ext_hopt=NULL; + RoutingHeader *ext_routing=NULL; + TCPHeader *tcp=NULL; + UDPHeader *udp=NULL; + ICMPv4Header *icmp4=NULL; + ICMPv6Header *icmp6=NULL; + EthernetHeader *eth=NULL; + ARPHeader *arp=NULL; + RawData *raw=NULL; + + /* Analyze the packet. This returns a list of header types and lengths */ + if((packetheaders=PacketParser::parse_packet(pkt, pktlen, eth_included))==NULL) + return NULL; + + /* Store each header in its own PacketHeader object type */ + for(int i=0; packetheaders[i].length!=0; i++){ + + switch(packetheaders[i].type){ + + case HEADER_TYPE_ETHERNET: + eth=new EthernetHeader(); + eth->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=eth; + }else{ + last->setNextElement(eth); + } + last=eth; + break; + + case HEADER_TYPE_ARP: + arp=new ARPHeader(); + arp->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=arp; + }else{ + last->setNextElement(arp); + } + last=arp; + break; + + case HEADER_TYPE_IPv4: + ip4=new IPv4Header(); + ip4->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=ip4; + }else{ + last->setNextElement(ip4); + } + last=ip4; + break; + + case HEADER_TYPE_IPv6: + ip6=new IPv6Header(); + ip6->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=ip6; + }else{ + last->setNextElement(ip6); + } + last=ip6; + break; + + case HEADER_TYPE_TCP: + tcp=new TCPHeader(); + tcp->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=tcp; + }else{ + last->setNextElement(tcp); + } + last=tcp; + break; + + case HEADER_TYPE_UDP: + udp=new UDPHeader(); + udp->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=udp; + }else{ + last->setNextElement(udp); + } + last=udp; + break; + + case HEADER_TYPE_ICMPv4: + icmp4=new ICMPv4Header(); + icmp4->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=icmp4; + }else{ + last->setNextElement(icmp4); + } + last=icmp4; + break; + + case HEADER_TYPE_ICMPv6: + icmp6=new ICMPv6Header(); + icmp6->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=icmp6; + }else{ + last->setNextElement(icmp6); + } + last=icmp6; + break; + + case HEADER_TYPE_IPv6_HOPOPT: + ext_hopt=new HopByHopHeader(); + ext_hopt->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=ext_hopt; + }else{ + last->setNextElement(ext_hopt); + } + last=ext_hopt; + break; + + case HEADER_TYPE_IPv6_ROUTE: + ext_routing=new RoutingHeader(); + ext_routing->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=ext_routing; + }else{ + last->setNextElement(ext_routing); + } + last=ext_routing; + break; + + case HEADER_TYPE_IPv6_FRAG: + ext_frag=new FragmentHeader(); + ext_frag->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=ext_frag; + }else{ + last->setNextElement(ext_frag); + } + last=ext_frag; + break; + + case HEADER_TYPE_IPv6_OPTS: + ext_dopts=new DestOptsHeader(); + ext_dopts->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=ext_dopts; + }else{ + last->setNextElement(ext_dopts); + } + last=ext_dopts; + break; + + case HEADER_TYPE_RAW_DATA: + default: + raw=new RawData(); + raw->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=raw; + }else{ + last->setNextElement(raw); + } + last=raw; + break; + } + curr_pkt+=packetheaders[i].length; + } + return first; +} /* End of split() */ + + +/* This method frees a chain of PacketElement objects. Note that objects in + * the chain are freed by calling "delete" on them, so only those instances + * that have been obtained through a call to "new" should be passed to this + * method. Chains returned by PacketParser::split() are safe to use with this.*/ +int PacketParser::freePacketChain(PacketElement *first){ + PacketElement *curr=first; + PacketElement *next=NULL; + while(curr!=NULL){ + next=curr->getNextElement(); + delete curr; + curr=next; + } + return OP_SUCCESS; +} /* End of freePacketChain() */ + + +/* This method is for debugging purposes only. It tests the packet parser and + * the PacketElement class family. Basically it checks that the supplied + * chain of PacketElements can be serialized and de-serialized correctly. + * Returns NULL on success or an error string in case of failure. */ +const char *PacketParser::test_packet_parser(PacketElement *test_pkt){ + const char *errmsg=NULL; + PacketElement *parsed_pkt=NULL; + PacketElement *orig_pkt=NULL; + PacketElement *new_pkt=NULL; + u8 *mypktbuff2=NULL; + u8 *mypktbuff=NULL; + + if(test_pkt==NULL){ + errmsg="NULL pointer supplied"; + goto end; + } + + /* Generate a serialized version of the packet */ + mypktbuff=(u8 *)safe_malloc(test_pkt->getLen()); + test_pkt->dumpToBinaryBuffer(mypktbuff, test_pkt->getLen()); + + /* Generate a chain of PacketElement objects from the serialized version. */ + parsed_pkt=PacketParser::split(mypktbuff, test_pkt->getLen()); + + if(parsed_pkt==NULL){ + errmsg="PacketParser::split() returned NULL"; + goto end; + } + if(parsed_pkt->getLen()!=test_pkt->getLen()){ + errmsg="Packets have different lengths"; + goto end; + } + + /* Generate a serialized version of the new chain */ + mypktbuff2=(u8 *)safe_malloc(parsed_pkt->getLen()); + parsed_pkt->dumpToBinaryBuffer(mypktbuff2, parsed_pkt->getLen()); + + /* Make sure both packets produce the exact same binary buffer */ + if(memcmp(mypktbuff, mypktbuff2, parsed_pkt->getLen())!=0){ + errmsg="The two packets do not result in the same binary buffer"; + goto end; + } + + /* Now let's check that both chains have the same number and type of + * PacketElements. */ + orig_pkt=test_pkt; + new_pkt=parsed_pkt; + while(orig_pkt!=NULL && new_pkt!=NULL){ + if(orig_pkt->protocol_id() != new_pkt->protocol_id() ){ + errmsg="Protocol IDs do not match"; + goto end; + } + orig_pkt=orig_pkt->getNextElement(); + new_pkt=new_pkt->getNextElement(); + } + + if(orig_pkt!=NULL || new_pkt!=NULL){ + errmsg="The two packets do not have the same number of chained elements."; + goto end; + } + + end: + /* Free our allocations */ + if(mypktbuff!=NULL) + free(mypktbuff); + if(mypktbuff2!=NULL) + free(mypktbuff2); + if(parsed_pkt!=NULL) + PacketParser::freePacketChain(parsed_pkt); + + /* If everything went well, errmsg should still be NULL. Otherwise it + * should point to an error message.*/ + return errmsg; +} + + + +/* Returns true if the supplied "rcvd" packet is a response to the "sent" packet. + * This method currently handles IPv4, IPv6, ICMPv4, ICMPv6, TCP and UDP. Here + * some examples of what can be matched using it: + * + * Probe: TCP SYN -> Response TCP SYN|ACK + * Probe: TCP SYN -> Response TCP RST|ACK + * Probe: UDP:53 -> Response UDP from port 53. + * Probe ICMP Echo -> Response ICMP Echo reply + * Probe ICMPv6 Neighbor Solicitation -> Response ICMPv6 Neighbor Advert + * Probe Malformed IPv6 -> Response ICMPv6 Parameter Problem + * Probe MLDv1 Query -> Response MLDv1 Report + * Probe ICMP Timestamp request -> Response ICMP timestamp response + * etc... + * + * Note that ICMP error messages are matched against sent probes (e.g: an ICMP + * Parameter Problem generated as a result of an invalid TCP segment is matched + * positively with the original TCP segment). Therefore, the caller must ensure + * that the received packet is what it expects before using it (e.g: the packet + * is an actual TCP packet, not an ICMP error). + * + * Warning: this method assumes that the probes you send are reasonably + * different from each other. Don't expect a 100% accuracy if you send a bunch + * of TCP segments with the same source and destination port numbers, or a + * bunch of ICMP messages with the same identifier and sequence number. */ +bool PacketParser::is_response(PacketElement *sent, PacketElement *rcvd){ + if(PKTPARSERDEBUG)printf("%s(): called\n", __func__); + + if(sent==NULL || rcvd==NULL) + return false; + + /* If any of the packets is encapsulated in an Ethernet frame, strip the + * link layer header before proceeding with the matching process. */ + if(rcvd->protocol_id()==HEADER_TYPE_ETHERNET) + if( (rcvd=rcvd->getNextElement())==NULL) + return false; + if(sent->protocol_id()==HEADER_TYPE_ETHERNET) + if( (sent=sent->getNextElement())==NULL) + return false; + + /* Make sure both packets have the same network layer */ + if(rcvd->protocol_id()!=sent->protocol_id()) + return false; + + /* The packet could be ARP */ + if(rcvd->protocol_id()==HEADER_TYPE_ARP){ + ARPHeader *sent_arp=(ARPHeader *)sent; + ARPHeader *rcvd_arp=(ARPHeader *)rcvd; + switch(sent_arp->getOpCode()){ + case OP_ARP_REQUEST: + if(rcvd_arp->getOpCode()==OP_ARP_REPLY){ + /* TODO @todo: getTargetIP() and getSenderIP() should + * either return struct in_addr or IPAddress but not u32. */ + if(sent_arp->getTargetIP()==rcvd_arp->getSenderIP()) + if(sent_arp->getSenderIP()==rcvd_arp->getTargetIP()) + return true; + } + return false; + break; + + /* We only support ARP, not RARP or other weird stuff. Also, if + * we didn't send a request, then we don't expect any response */ + case OP_RARP_REQUEST: + case OP_DRARP_REQUEST: + case OP_INARP_REQUEST: + default: + return false; + break; + + } + return false; + } + + /* The packet is IPv4 or IPv6 */ + if(rcvd->protocol_id()!=HEADER_TYPE_IPv6 && rcvd->protocol_id()!=HEADER_TYPE_IPv4) + return false; + if(PKTPARSERDEBUG)printf("%s(): Both packets use IP.\n", __func__); + + /* Handle the network layer with a more specific class */ + NetworkLayerElement *rcvd_ip=(NetworkLayerElement *)rcvd; + NetworkLayerElement *sent_ip=(NetworkLayerElement *)sent; + + /* Ensure the packet comes from the host we sent the probe to */ + if( memcmp(rcvd_ip->getSourceAddress(), sent_ip->getDestinationAddress(), rcvd_ip->getAddressLength())!=0 ) + return false; + /* Ensure the received packet is destined to us */ + if( memcmp(rcvd_ip->getDestinationAddress(), sent_ip->getSourceAddress(), rcvd_ip->getAddressLength())!=0 ) + return false; + + if(PKTPARSERDEBUG)printf("%s(): Src and Dst addresses make sense.\n", __func__); + + /* Skip layers until we find ICMP or a transport protocol */ + PacketElement *rcvd_layer4=rcvd_ip->getNextElement(); + PacketElement *sent_layer4=sent_ip->getNextElement(); + while(rcvd_layer4!=NULL){ + if(rcvd_layer4->protocol_id()==HEADER_TYPE_UDP || rcvd_layer4->protocol_id()==HEADER_TYPE_TCP || + rcvd_layer4->protocol_id()==HEADER_TYPE_ICMPv4 || rcvd_layer4->protocol_id()==HEADER_TYPE_ICMPv6 ){ + break; + }else{ + rcvd_layer4=rcvd_layer4->getNextElement(); + } + } + while(sent_layer4!=NULL){ + if(sent_layer4->protocol_id()==HEADER_TYPE_UDP || sent_layer4->protocol_id()==HEADER_TYPE_TCP || + sent_layer4->protocol_id()==HEADER_TYPE_ICMPv4 || sent_layer4->protocol_id()==HEADER_TYPE_ICMPv6 ){ + break; + }else{ + sent_layer4=sent_layer4->getNextElement(); + } + } + if(rcvd_layer4==NULL || sent_layer4==NULL) + return false; + + if(PKTPARSERDEBUG)printf("%s(): Layer 4 found for both packets.\n", __func__); + + /* If we get here it means that both packets have a proper layer4 protocol + * header. Now we have to check which type are they and see if a probe-response + * relation can be established. */ + if(sent_layer4->protocol_id()==HEADER_TYPE_ICMPv6 || sent_layer4->protocol_id()==HEADER_TYPE_ICMPv4){ + + if(PKTPARSERDEBUG)printf("%s(): Sent packet is ICMP.\n", __func__); + + /* Make sure received packet is ICMP (we only expect ICMP responses for + * ICMP probes) */ + if(rcvd_layer4->protocol_id()!=HEADER_TYPE_ICMPv6 && rcvd_layer4->protocol_id()!=HEADER_TYPE_ICMPv4 ) + return false; + + /* Make sure both packets have the same ICMP version */ + if(sent_layer4->protocol_id()!=rcvd_layer4->protocol_id()) + return false; + + if(PKTPARSERDEBUG)printf("%s(): Received packet is ICMP too.\n", __func__); + + /* Check if the received ICMP is an error message. We don't care which kind + * of error message it is. The only important thing is that error messages + * contain a copy of the original datagram, and that's what we want to + * match against the sent probe. */ + if( ((ICMPHeader *)rcvd_layer4)->isError() ){ + NetworkLayerElement *iperror=(NetworkLayerElement *)rcvd_layer4->getNextElement(); + + if(PKTPARSERDEBUG)printf("%s(): Received ICMP is an error message.\n", __func__); + + /* ICMP error message must contain the original datagram */ + if(iperror==NULL) + return false; + + /* The first header must be IP */ + if(iperror->protocol_id()!=HEADER_TYPE_IPv6 && iperror->protocol_id()!=HEADER_TYPE_IPv4) + return false; + + /* The IP version must match the probe's */ + if(iperror->protocol_id()!=sent_ip->protocol_id()) + return false; + + /* Source and destination addresses must match the probe's */ + if( memcmp(iperror->getSourceAddress(), sent_ip->getSourceAddress(), iperror->getAddressLength())!=0 ) + return false; + if( memcmp(iperror->getDestinationAddress(), sent_ip->getDestinationAddress(), iperror->getAddressLength())!=0 ) + return false; + + /* So far we've verified that the ICMP error contains an IP datagram that matches + * what we sent. Now, let's find the upper layer ICMP header (skip extension + * headers until we find ICMP) */ + ICMPHeader *inner_icmp=(ICMPHeader *)iperror->getNextElement(); + while(inner_icmp!=NULL){ + if(inner_icmp->protocol_id()==HEADER_TYPE_ICMPv4 || inner_icmp->protocol_id()==HEADER_TYPE_ICMPv6 ){ + break; + }else{ + inner_icmp=(ICMPHeader *)inner_icmp->getNextElement(); + } + } + if(inner_icmp==NULL) + return false; + + /* If we get here it means that we've found an ICMP header inside the + * ICMP error message that we received. First of all, check that the + * ICMP version matches what we sent. */ + if(sent_layer4->protocol_id() != inner_icmp->protocol_id()) + return false; + + /* Make sure ICMP type and code match */ + if( ((ICMPHeader*)sent_layer4)->getType() != inner_icmp->getType() ) + return false; + if( ((ICMPHeader*)sent_layer4)->getCode() != inner_icmp->getCode() ) + return false; + + /* Now go into a bit of detail and try to determine if both headers + * are equal, comparing the values of specific fields. */ + if(sent_layer4->protocol_id()==HEADER_TYPE_ICMPv6){ + ICMPv6Header *sent_icmp6=(ICMPv6Header *)sent_layer4; + ICMPv6Header *inner_icmp6=(ICMPv6Header *)inner_icmp; + + switch(sent_icmp6->getType()){ + case ICMPv6_UNREACH: + case ICMPv6_TIMXCEED : + /* For these we cannot guarantee that the received ICMPv6 error + * packet included data beyond the inner ICMPv6 header, so we just + * assume that they are a match to the sent probe. (We shouldn't + * really be sending ICMPv6 error messages and expect ICMPv6 error + * responses that contain our ICMv6P error messages, should we? + * Well, even if we do, there is a good chance we are able to match + * those responses with the original probe) */ + break; + + case ICMPv6_PKTTOOBIG: + if(sent_icmp6->getMTU() != inner_icmp6->getMTU()) + return false; + break; + + case ICMPv6_PARAMPROB: + if(sent_icmp6->getPointer() != inner_icmp6->getPointer()) + return false; + break; + + case ICMPv6_ECHO: + case ICMPv6_ECHOREPLY: + if(sent_icmp6->getIdentifier() != inner_icmp6->getIdentifier()) + return false; + if(sent_icmp6->getSequence() != inner_icmp6->getSequence()) + return false; + break; + + case ICMPv6_ROUTERSOLICIT: + /* Here we do not have much to compare, so we just test that + * the reserved field contains the same value, usually zero. */ + if(sent_icmp6->getReserved()!=inner_icmp6->getReserved()) + return false; + break; + + case ICMPv6_ROUTERADVERT: + if(sent_icmp6->getCurrentHopLimit() != inner_icmp6->getCurrentHopLimit() ) + return false; + if(sent_icmp6->getRouterLifetime() != inner_icmp6->getRouterLifetime() ) + return false; + if(sent_icmp6->getReachableTime() != inner_icmp6->getReachableTime() ) + return false; + if(sent_icmp6->getRetransmissionTimer() != inner_icmp6->getRetransmissionTimer() ) + return false; + break; + + case ICMPv6_REDIRECT: + if( memcmp(sent_icmp6->getTargetAddress().s6_addr, inner_icmp6->getTargetAddress().s6_addr, 16) !=0 ) + return false; + if( memcmp(sent_icmp6->getDestinationAddress().s6_addr, inner_icmp6->getDestinationAddress().s6_addr, 16) !=0 ) + return false; + break; + + case ICMPv6_NGHBRSOLICIT: + case ICMPv6_NGHBRADVERT: + if( memcmp(sent_icmp6->getTargetAddress().s6_addr, inner_icmp6->getTargetAddress().s6_addr, 16) !=0 ) + return false; + break; + + case ICMPv6_RTRRENUM: + if(sent_icmp6->getSequence() != inner_icmp6->getSequence() ) + return false; + if(sent_icmp6->getSegmentNumber() != inner_icmp6->getSegmentNumber() ) + return false; + if(sent_icmp6->getMaxDelay() != inner_icmp6->getMaxDelay() ) + return false; + if(sent_icmp6->getFlags() != inner_icmp6->getFlags() ) + return false; + break; + + case ICMPv6_NODEINFOQUERY: + case ICMPv6_NODEINFORESP: + if(sent_icmp6->getNodeInfoFlags() != inner_icmp6->getNodeInfoFlags() ) + return false; + if(sent_icmp6->getNonce() != inner_icmp6->getNonce()) + return false; + if(sent_icmp6->getQtype() != inner_icmp6->getQtype() ) + return false; + break; + + + case ICMPv6_GRPMEMBQUERY: + case ICMPv6_GRPMEMBREP: + case ICMPv6_GRPMEMBRED: + case ICMPv6_INVNGHBRSOLICIT: + case ICMPv6_INVNGHBRADVERT: + case ICMPv6_MLDV2: + case ICMPv6_AGENTDISCOVREQ: + case ICMPv6_AGENTDISCOVREPLY: + case ICMPv6_MOBPREFIXSOLICIT: + case ICMPv6_MOBPREFIXADVERT: + case ICMPv6_CERTPATHSOLICIT: + case ICMPv6_CERTPATHADVERT: + case ICMPv6_EXPMOBILITY: + case ICMPv6_MRDADVERT: + case ICMPv6_MRDSOLICIT: + case ICMPv6_MRDTERMINATE: + case ICMPv6_FMIPV6: + /* All these types are not currently implemented but since the + * sent_icmp.getType() has returned such type, we assume + * that there is a match (don't return false here). */ + break; + + default: + /* Do not match ICMPv6 types we don't know about */ + return false; + break; + } + }else if(sent_layer4->protocol_id()==HEADER_TYPE_ICMPv4){ + ICMPv4Header *sent_icmp4=(ICMPv4Header *)sent_layer4; + ICMPv4Header *inner_icmp4=(ICMPv4Header *)inner_icmp; + + switch(sent_icmp4->getType()){ + case ICMP_ECHOREPLY: + case ICMP_ECHO: + case ICMP_TSTAMP: + case ICMP_TSTAMPREPLY: + case ICMP_INFO: + case ICMP_INFOREPLY: + case ICMP_MASK: + case ICMP_MASKREPLY: + case ICMP_DOMAINNAME: + case ICMP_DOMAINNAMEREPLY: + /* Check the message identifier and sequence number */ + if(sent_icmp4->getIdentifier() != inner_icmp4->getIdentifier()) + return false; + if(sent_icmp4->getSequence() != inner_icmp4->getSequence()) + return false; + break; + + case ICMP_ROUTERADVERT: + /* Check only the main fields, no need to parse the whole list + * of addresses (maybe we didn't even get enough octets to + * check that). */ + if(sent_icmp4->getNumAddresses() != inner_icmp4->getNumAddresses() ) + return false; + if(sent_icmp4->getAddrEntrySize() != inner_icmp4->getAddrEntrySize()) + return false; + if(sent_icmp4->getLifetime() != inner_icmp4->getLifetime() ) + return false; + break; + + case ICMP_ROUTERSOLICIT: + /* Here we do not have much to compare, so we just test that + * the reserved field contains the same value, usually zero. */ + if(sent_icmp4->getReserved()!=inner_icmp4->getReserved()) + return false; + break; + + case ICMP_UNREACH: + case ICMP_SOURCEQUENCH: + case ICMP_TIMXCEED: + /* For these we cannot guarantee that the received ICMP error + * packet included data beyond the inner ICMP header, so we just + * assume that they are a match to the sent probe. (We shouldn't + * really be sending ICMP error messages and expect ICMP error + * responses that contain our ICMP error messages, should we? + * Well, even if we do, there is a good chance we are able to match + * those responses with the original probe) */ + break; + + case ICMP_REDIRECT: + if(sent_icmp4->getGatewayAddress().s_addr != inner_icmp4->getGatewayAddress().s_addr) + return false; + break; + + case ICMP_PARAMPROB: + if(sent_icmp4->getParameterPointer() != inner_icmp4->getParameterPointer()) + return false; + break; + + case ICMP_TRACEROUTE: + if(sent_icmp4->getIDNumber() != inner_icmp4->getIDNumber()) + return false; + if(sent_icmp4->getOutboundHopCount() != inner_icmp4->getOutboundHopCount()) + return false; + if(sent_icmp4->getOutputLinkSpeed() != inner_icmp4->getOutputLinkSpeed() ) + return false; + if(sent_icmp4->getOutputLinkMTU() != inner_icmp4->getOutputLinkMTU() ) + return false; + break; + + case ICMP_SECURITYFAILURES: + /* Check the pointer and the reserved field */ + if(sent_icmp4->getSecurityPointer() != inner_icmp4->getSecurityPointer()) + return false; + if(sent_icmp4->getReserved() != inner_icmp4->getReserved()) + return false; + break; + + default: + /* Do not match ICMP types we don't know about */ + return false; + break; + } + }else{ + return false; // Should never happen, though. + } + }else{ /* Received ICMP is informational. */ + + if(PKTPARSERDEBUG)printf("%s(): Received ICMP is an informational message.\n", __func__); + + /* If we get here it means that we received an informational ICMPv6 + * message. So now we have to check if the received message is the + * expected reply to the probe we sent (like an Echo reply for an Echo + * request, etc). */ + + if(sent_layer4->protocol_id()==HEADER_TYPE_ICMPv6 && rcvd_layer4->protocol_id()==HEADER_TYPE_ICMPv6){ + ICMPv6Header *sent_icmp6=(ICMPv6Header *)sent_layer4; + ICMPv6Header *rcvd_icmp6=(ICMPv6Header *)rcvd_layer4; + + switch( sent_icmp6->getType() ){ + + case ICMPv6_UNREACH: + case ICMPv6_TIMXCEED : + case ICMPv6_PKTTOOBIG: + case ICMPv6_PARAMPROB: + /* This should never happen. If we got here, the received type + * should be of an informational message, not an error message. */ + printf("Error in isResponse()\n"); + return false; + break; + + case ICMPv6_ECHO: + /* For Echo request, we expect echo replies */ + if(rcvd_icmp6->getType()!=ICMPv6_ECHOREPLY) + return false; + /* And we expect the ID and sequence number of the reply to + * match the ID and seq of the request. */ + if(sent_icmp6->getIdentifier() != rcvd_icmp6->getIdentifier()) + return false; + if(sent_icmp6->getSequence() != rcvd_icmp6->getSequence()) + return false; + break; + + case ICMPv6_ECHOREPLY: + /* We don't expect replies to Echo replies */ + return false; + break; + + case ICMPv6_ROUTERSOLICIT: + /* For Router solicitations, we expect Router advertisements. + * We only check if the received ICMP is a router advert because + * there is nothing else that can be used to match the solicitation + * with the response. */ + if(rcvd_icmp6->getType()!=ICMPv6_ROUTERADVERT) + return false; + break; + + case ICMPv6_ROUTERADVERT: + /* We don't expect replies to router advertisements */ + return false; + break; + + case ICMPv6_REDIRECT: + /* We don't expect replies to Redirect messages */ + return false; + break; + + case ICMPv6_NGHBRSOLICIT: + if(PKTPARSERDEBUG)printf("%s(): Sent ICMP is an ICMPv6 Neighbor Solicitation.\n", __func__); + /* For Neighbor solicitations, we expect Neighbor advertisements + * with the "S" flag set (solicited flag) and the same address + * in the "TargetAddress" field. */ + if(rcvd_icmp6->getType()!=ICMPv6_NGHBRADVERT) + return false; + if(PKTPARSERDEBUG)printf("%s(): Received ICMP is an ICMPv6 Neighbor Advertisement.\n", __func__); + if( !(rcvd_icmp6->getFlags() & 0x40) ) + return false; + if( memcmp(sent_icmp6->getTargetAddress().s6_addr, rcvd_icmp6->getTargetAddress().s6_addr, 16) !=0 ) + return false; + break; + + case ICMPv6_NGHBRADVERT: + /* We don't expect replies to Neighbor advertisements */ + return false; + break; + + case ICMPv6_NODEINFOQUERY: + /* For Node Information Queries we expect Node Information + * responses with the same Nonce value that we used in the query. */ + if(rcvd_icmp6->getType()!=ICMPv6_NODEINFORESP) + return false; + if(sent_icmp6->getNonce() != rcvd_icmp6->getNonce()) + return false; + break; + + case ICMPv6_NODEINFORESP: + /* Obviously, we do not expect responses to a response */ + return false; + break; + + case ICMPv6_INVNGHBRSOLICIT: + /* For Inverse Neighbor Discovery Solicitations we expect + * advertisements in response. We don't do any additional + * validation since any advert can be considered a response + * to the solicitation. */ + if(rcvd_icmp6->getType()!=ICMPv6_INVNGHBRADVERT) + return false; + break; + + case ICMPv6_INVNGHBRADVERT: + /* We don't expect responses to advertisements */ + return false; + break; + + + case ICMPv6_RTRRENUM: + /* We don't expect specific responses to router renumbering + * messages. */ + return false; + break; + + case ICMPv6_GRPMEMBQUERY: + /* For Multicast Listener Discovery (MLD) queries, we expect + * either MLD Responses or MLD Done messages. We can't handle MLDv2 + * yet, so we don't match it. TODO: Implement support for MLDv2 */ + if(rcvd_icmp6->getType()!=ICMPv6_GRPMEMBREP && rcvd_icmp6->getType()!=ICMPv6_GRPMEMBRED) + return false; + /* Now we have two possibilities: + * a) The query is a "General Query" where the multicast address + * is set to zero. + * b) The query is a "Multicast-Address-Specific Query", where + * the multicast address field is set to an actual multicast + * address. + * In the first case, we match any query response to the request, + * as we don't have a multicast address to compare. In the second + * case, we verify that the target mcast address of the query + * matches the one in the response. */ + struct in6_addr zeroaddr; + memset(&zeroaddr, 0, sizeof(struct in6_addr)); + if( memcmp( sent_icmp6->getMulticastAddress().s6_addr, zeroaddr.s6_addr, 16) != 0 ){ /* Case B: */ + if (memcmp( sent_icmp6->getMulticastAddress().s6_addr, rcvd_icmp6->getMulticastAddress().s6_addr, 16)!=0 ) + return false; + } + break; + + case ICMPv6_GRPMEMBREP: + case ICMPv6_GRPMEMBRED: + /* We don't expect responses to MLD reports */ + return false; + break; + + case ICMPv6_MLDV2: + case ICMPv6_AGENTDISCOVREQ: + case ICMPv6_AGENTDISCOVREPLY: + case ICMPv6_MOBPREFIXSOLICIT: + case ICMPv6_MOBPREFIXADVERT: + case ICMPv6_CERTPATHSOLICIT: + case ICMPv6_CERTPATHADVERT: + case ICMPv6_EXPMOBILITY: + case ICMPv6_MRDADVERT: + case ICMPv6_MRDSOLICIT: + case ICMPv6_MRDTERMINATE: + case ICMPv6_FMIPV6: + default: + /* Do not match ICMPv6 types we don't implement or know about * + * TODO: Implement these ICMPv6 types. */ + return false; + break; + + } + + }else if(sent_layer4->protocol_id()==HEADER_TYPE_ICMPv4 && rcvd_layer4->protocol_id()==HEADER_TYPE_ICMPv4){ + ICMPv4Header *sent_icmp4=(ICMPv4Header *)sent_layer4; + ICMPv4Header *rcvd_icmp4=(ICMPv4Header *)rcvd_layer4; + + switch( sent_icmp4->getType() ){ + + case ICMP_ECHOREPLY: + /* We don't expect replies to Echo replies. */ + return false; + break; + + case ICMP_UNREACH: + case ICMP_SOURCEQUENCH: + case ICMP_REDIRECT: + case ICMP_TIMXCEED: + case ICMP_PARAMPROB: + /* Nodes are not supposed to respond to error messages, so + * we don't expect any replies. */ + return false; + break; + + case ICMP_ECHO: + /* For Echo request, we expect echo replies */ + if(rcvd_icmp4->getType()!=ICMP_ECHOREPLY) + return false; + /* And we expect the ID and sequence number of the reply to + * match the ID and seq of the request. */ + if(sent_icmp4->getIdentifier() != rcvd_icmp4->getIdentifier()) + return false; + if(sent_icmp4->getSequence() != rcvd_icmp4->getSequence()) + return false; + break; + + case ICMP_ROUTERSOLICIT: + /* For ICMPv4 router solicitations, we expect router advertisements. + * We don't validate anything else because in IPv4 any advert that + * comes from the host we sent the solicitation to can be + * considered a response. */ + if(rcvd_icmp4->getType()!=ICMP_ROUTERADVERT) + return false; + break; + + case ICMP_ROUTERADVERT: + /* We don't expect responses to advertisements */ + return false; + break; + + case ICMP_TSTAMP: + /* For Timestampt requests, we expect timestamp replies */ + if(rcvd_icmp4->getType()!=ICMP_TSTAMPREPLY) + return false; + /* And we expect the ID and sequence number of the reply to + * match the ID and seq of the request. */ + if(sent_icmp4->getIdentifier() != rcvd_icmp4->getIdentifier()) + return false; + if(sent_icmp4->getSequence() != rcvd_icmp4->getSequence()) + return false; + break; + + case ICMP_TSTAMPREPLY: + /* We do not expect responses to timestamp replies */ + return false; + break; + + case ICMP_INFO: + /* For Information requests, we expect Information replies */ + if(rcvd_icmp4->getType()!=ICMP_INFOREPLY) + return false; + /* And we expect the ID and sequence number of the reply to + * match the ID and seq of the request. */ + if(sent_icmp4->getIdentifier() != rcvd_icmp4->getIdentifier()) + return false; + if(sent_icmp4->getSequence() != rcvd_icmp4->getSequence()) + return false; + break; + + case ICMP_INFOREPLY: + /* We do not expect responses to Information replies */ + return false; + break; + + case ICMP_MASK: + /* For Netmask requests, we expect Netmask replies */ + if(rcvd_icmp4->getType()!=ICMP_MASKREPLY) + return false; + /* And we expect the ID and sequence number of the reply to + * match the ID and seq of the request. */ + if(sent_icmp4->getIdentifier() != rcvd_icmp4->getIdentifier()) + return false; + if(sent_icmp4->getSequence() != rcvd_icmp4->getSequence()) + return false; + break; + + case ICMP_MASKREPLY: + /* We do not expect responses to netmask replies */ + return false; + break; + + case ICMP_TRACEROUTE: + /* We don't expect replies to a traceroute message as it is + * sent as a response to an IP datagram that contains the + * IP traceroute option. Also, note that this function does + * not take this into account when processing IPv4 datagrams + * so if we receive an ICMP_TRACEROUTE we'll not be able + * to match it with the original IP datagram. */ + return false; + break; + + case ICMP_DOMAINNAME: + /* For Domain Name requests, we expect Domain Name replies */ + if(rcvd_icmp4->getType()!=ICMP_DOMAINNAMEREPLY) + return false; + /* And we expect the ID and sequence number of the reply to + * match the ID and seq of the request. */ + if(sent_icmp4->getIdentifier() != rcvd_icmp4->getIdentifier()) + return false; + if(sent_icmp4->getSequence() != rcvd_icmp4->getSequence()) + return false; + break; + + case ICMP_DOMAINNAMEREPLY: + /* We do not expect replies to DN replies */ + return false; + break; + + case ICMP_SECURITYFAILURES: + /* Nodes are not expected to send replies to this message, as it + * is an ICMP error. */ + return false; + break; + } + }else{ + return false; // Should never happen + } + } + }else if(sent_layer4->protocol_id()==HEADER_TYPE_TCP || sent_layer4->protocol_id()==HEADER_TYPE_UDP){ + + if(PKTPARSERDEBUG)printf("%s(): Sent packet has a transport layer header.\n", __func__); + + /* Both are TCP or both UDP */ + if(sent_layer4->protocol_id()==rcvd_layer4->protocol_id()){ + + if(PKTPARSERDEBUG)printf("%s(): Received packet has a transport layer header too.\n", __func__); + + /* Probe source port must equal response target port */ + if( ((TransportLayerElement *)sent_layer4)->getSourcePort() != ((TransportLayerElement *)rcvd_layer4)->getDestinationPort() ) + return false; + /* Probe target port must equal response source port */ + if( ((TransportLayerElement *)rcvd_layer4)->getSourcePort() != ((TransportLayerElement *)sent_layer4)->getDestinationPort() ) + return false; + + /* If we sent TCP or UDP and got ICMP in response, we need to find a copy of our packet in the + * ICMP payload, providing it is an ICMP error message. */ + }else if(rcvd_layer4->protocol_id()==HEADER_TYPE_ICMPv6 || rcvd_layer4->protocol_id()==HEADER_TYPE_ICMPv4){ + + if(PKTPARSERDEBUG)printf("%s(): Received packet does not have transport layer header but an ICMP header.\n", __func__); + + /* We only expect ICMP error messages */ + if( !(((ICMPHeader *)rcvd_layer4)->isError()) ) + return false; + + /* Let's validate the original header */ + NetworkLayerElement *iperror=(NetworkLayerElement *)rcvd_layer4->getNextElement(); + + /* ICMP error message must contain the original datagram */ + if(iperror==NULL) + return false; + + /* The first header must be IP */ + if(iperror->protocol_id()!=HEADER_TYPE_IPv6 && iperror->protocol_id()!=HEADER_TYPE_IPv4) + return false; + + /* The IP version must match the probe's */ + if(iperror->protocol_id()!=sent_ip->protocol_id()) + return false; + + /* Source and destination addresses must match the probe's (NATs are + * supposed to rewrite them too, so this should be OK) */ + if( memcmp(iperror->getSourceAddress(), sent_ip->getSourceAddress(), iperror->getAddressLength())!=0 ) + return false; + if( memcmp(iperror->getDestinationAddress(), sent_ip->getDestinationAddress(), iperror->getAddressLength())!=0 ) + return false; + + /* So far we've verified that the ICMP error contains an IP datagram that matches + * what we sent. Now, let's find the upper layer protocol (skip extension + * headers and the like until we find some transport protocol). */ + TransportLayerElement *layer4error=(TransportLayerElement *)iperror->getNextElement(); + while(layer4error!=NULL){ + if(layer4error->protocol_id()==HEADER_TYPE_UDP || layer4error->protocol_id()==HEADER_TYPE_TCP ){ + break; + }else{ + layer4error=(TransportLayerElement *)layer4error->getNextElement(); + } + } + if(layer4error==NULL) + return false; + + /* Now make sure we see the same port numbers */ + if( layer4error->getSourcePort() != ((TransportLayerElement *)sent_layer4)->getSourcePort() ) + return false; + if( layer4error->getDestinationPort() != ((TransportLayerElement *)sent_layer4)->getDestinationPort() ) + return false; + } else { + return false; + } + }else{ + /* We sent a layer 4 other than ICMP, ICMPv6, TCP, or UDP. We return false + * as we cannot match responses for protocols we don't understand */ + return false; + } + + /* If we get there it means the packet passed all the tests. Return true + * to indicate that the packet is a response to this FPProbe. */ + if(PKTPARSERDEBUG)printf("%s(): The received packet was successfully matched with the sent packet.\n", __func__); + return true; +} + +/* Tries to find a transport layer header in the supplied chain of + * protocol headers. On success it returns a pointer to a PacketElement + * of one of these types: + * + * HEADER_TYPE_TCP + * HEADER_TYPE_UDP + * HEADER_TYPE_ICMPv4 + * HEADER_TYPE_ICMPv6 + * HEADER_TYPE_SCTP + * HEADER_TYPE_ARP + * + * It returns NULL if no transport layer header is found. + * + * Note that this method only understands IPv4, IPv6 (and its + * extension headers) and Ethernet. If the supplied packet contains + * something different before the tranport layer, NULL will be returned. + * */ +PacketElement *PacketParser::find_transport_layer(PacketElement *chain){ + PacketElement *aux=chain; + /* Traverse the chain of PacketElements */ + while(aux!=NULL){ + switch(aux->protocol_id()){ + /* If we have a link or a network layer header, skip it. */ + case HEADER_TYPE_IPv6_HOPOPT: + case HEADER_TYPE_IPv4: + case HEADER_TYPE_IPv6: + case HEADER_TYPE_IPv6_ROUTE: + case HEADER_TYPE_IPv6_FRAG: + case HEADER_TYPE_IPv6_NONXT: + case HEADER_TYPE_IPv6_OPTS: + case HEADER_TYPE_ETHERNET: + case HEADER_TYPE_IPv6_MOBILE: + aux=aux->getNextElement(); + break; + + /* If we found the transport layer, return it. */ + case HEADER_TYPE_TCP: + case HEADER_TYPE_UDP: + case HEADER_TYPE_ICMPv4: + case HEADER_TYPE_ICMPv6: + case HEADER_TYPE_SCTP: + case HEADER_TYPE_ARP: + return aux; + break; + + /* Otherwise, the packet contains headers we don't understand + * so we just return NULL to indicate that no valid transport + * layer was found. */ + default: + return NULL; + break; + } + } + return NULL; +} /* End of find_transport_layer() */ diff --git a/libnetutil/PacketParser.h b/libnetutil/PacketParser.h new file mode 100644 index 0000000..b5bf821 --- /dev/null +++ b/libnetutil/PacketParser.h @@ -0,0 +1,132 @@ +/*************************************************************************** + * PacketParser.h -- The PacketParser Class offers methods to parse * + * received network packets. Its main purpose is to facilitate the * + * conversion of raw sequences of bytes into chains of objects of the * + * PacketElement family. * + * * + ***********************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. */ + +#ifndef __PACKETPARSER_H__ +#define __PACKETPARSER_H__ 1 + +#include "ApplicationLayerElement.h" +#include "ARPHeader.h" +#include "DataLinkLayerElement.h" +#include "EthernetHeader.h" +#include "ICMPHeader.h" +#include "ICMPv4Header.h" +#include "ICMPv6Header.h" +#include "ICMPv6Option.h" +#include "ICMPv6RRBody.h" +#include "IPv4Header.h" +#include "IPv6Header.h" +#include "NetworkLayerElement.h" +#include "PacketElement.h" +#include "RawData.h" +#include "TCPHeader.h" +#include "TransportLayerElement.h" +#include "UDPHeader.h" +#include "HopByHopHeader.h" +#include "DestOptsHeader.h" +#include "FragmentHeader.h" +#include "RoutingHeader.h" + + +#define LINK_LAYER 2 +#define NETWORK_LAYER 3 +#define TRANSPORT_LAYER 4 +#define APPLICATION_LAYER 5 +#define EXTHEADERS_LAYER 6 + +typedef struct header_type_string{ + u32 type; + const char *str; +}header_type_string_t; + + +typedef struct packet_type{ + u32 type; + u32 length; +}pkt_type_t; + + +class PacketParser { + + private: + + public: + + /* Misc */ + PacketParser(); + ~PacketParser(); + void reset(); + + static const char *header_type2string(int val); + static pkt_type_t *parse_packet(const u8 *pkt, size_t pktlen, bool eth_included); + static int dummy_print_packet_type(const u8 *pkt, size_t pktlen, bool eth_included); /* TODO: remove */ + static int dummy_print_packet(const u8 *pkt, size_t pktlen, bool eth_included); /* TODO: remove */ + static int payload_offset(const u8 *pkt, size_t pktlen, bool link_included); + static PacketElement *split(const u8 *pkt, size_t pktlen, bool eth_included); + static PacketElement *split(const u8 *pkt, size_t pktlen); + static int freePacketChain(PacketElement *first); + static const char *test_packet_parser(PacketElement *test_pkt); + static bool is_response(PacketElement *sent, PacketElement *rcvd); + static PacketElement *find_transport_layer(PacketElement *chain); + +}; /* End of class PacketParser */ + +#endif /* __PACKETPARSER_H__ */ diff --git a/libnetutil/RawData.cc b/libnetutil/RawData.cc new file mode 100644 index 0000000..92500c5 --- /dev/null +++ b/libnetutil/RawData.cc @@ -0,0 +1,172 @@ +/*************************************************************************** + * RawData.cc -- The RawData Class represents a network packet payload. It * + * is essentially a single buffer that may contain either random data or * + * caller supplied data. This class can be used, for example, to be linked * + * to a UDP datagram. * + * * + ***********************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 "RawData.h" + +/******************************************************************************/ +/* CONTRUCTORS, DESTRUCTORS AND INITIALIZATION METHODS */ +/******************************************************************************/ +RawData::RawData(){ + this->reset(); +} /* End of RawData contructor */ + + +RawData::~RawData(){ + if(this->data!=NULL){ + free(this->data); + this->data=NULL; + } +} /* End of RawData destructor */ + + +/** Sets every attribute to its default value */ +void RawData::reset(){ + this->data=NULL; + this->length=0; +} /* End of reset() */ + + +/******************************************************************************/ +/* PacketElement:: OVERWRITTEN METHODS */ +/******************************************************************************/ + +u8 * RawData::getBufferPointer(){ + return this->getBufferPointer(NULL); +} /* End of getBufferPointer() */ + + +u8 * RawData::getBufferPointer(int *mylen){ + if(mylen!=NULL) + *mylen=this->length; + return this->data; +} /* End of getBufferPointer() */ + + +/** Added for consistency with the rest of classes of the PacketElement family. */ +int RawData::storeRecvData(const u8 *buf, size_t len){ + return this->store(buf, len); +} /* 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 RawData::protocol_id() const { + return HEADER_TYPE_RAW_DATA; +} /* 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. + * @return the length, in bytes, of the header, if its found to be valid or + * OP_FAILURE (-1) otherwise. */ +int RawData::validate(){ + 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 RawData::print(FILE *output, int detail) const { + fprintf(output, "Payload["); + fprintf(output, "%d byte%s]", this->length, (this->length!=1)? "s":""); + if(this->next!=NULL){ + print_separator(output, detail); + next->print(output, detail); + } + return OP_SUCCESS; +} /* End of print() */ + + +/******************************************************************************/ +/* PROTOCOL-SPECIFIC METHODS */ +/******************************************************************************/ + +int RawData::store(const u8 *buf, size_t len){ + /* If buffer had already been set, try to reuse it. */ + if(this->data!=NULL){ + if( this->length >= (int)len ){ + memcpy(this->data, buf, len); + this->length=(int)len; + return OP_SUCCESS; + }else{ + free(this->data); + } + } + if( (this->data=(u8 *)calloc(len, sizeof(u8)))==NULL ) + return OP_FAILURE; + memcpy(this->data, buf, len); + this->length=(int)len; + return OP_SUCCESS; +} /* End of store() */ + + +int RawData::store(const char *str){ + if(str==NULL) + return OP_FAILURE; + else + return this->store((const u8*)str, strlen(str)); +} /* End of store() */ + + diff --git a/libnetutil/RawData.h b/libnetutil/RawData.h new file mode 100644 index 0000000..545e010 --- /dev/null +++ b/libnetutil/RawData.h @@ -0,0 +1,90 @@ +/*************************************************************************** + * RawData.h -- The RawData Class represents a network packet payload. It * + * is essentially a single buffer that may contain either random data or * + * caller supplied data. This class can be used, for example, to be linked * + * to a UDP datagram. * + * * + ***********************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. */ + +#ifndef RAWDATA_H +#define RAWDATA_H 1 + +#include "ApplicationLayerElement.h" + + +class RawData : public ApplicationLayerElement { + + private: + u8 *data; + + public: + RawData(); + ~RawData(); + void reset(); + u8 *getBufferPointer(); + int storeRecvData(const u8 *buf, size_t len); + int protocol_id() const; + int validate(); + int print(FILE *output, int detail) const; + + u8 *getBufferPointer(int *mylen); + int store(const u8 *buf, size_t len); + int store(const char *str); + +}; + +#endif diff --git a/libnetutil/RoutingHeader.cc b/libnetutil/RoutingHeader.cc new file mode 100644 index 0000000..b978163 --- /dev/null +++ b/libnetutil/RoutingHeader.cc @@ -0,0 +1,313 @@ +/*************************************************************************** + * RoutingHeader.cc -- The RoutingHeader Class represents an IPv6 Routing * + * extension header. * + ***********************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 "RoutingHeader.h" +#include <assert.h> + +/******************************************************************************/ +/* CONTRUCTORS, DESTRUCTORS AND INITIALIZATION METHODS */ +/******************************************************************************/ +RoutingHeader::RoutingHeader() { + this->reset(); +} /* End of RoutingHeader constructor */ + + +RoutingHeader::~RoutingHeader() { + +} /* End of RoutingHeader destructor */ + + +/** Sets every attribute to its default value */ +void RoutingHeader::reset(){ + memset(&this->h, 0, sizeof(nping_ipv6_ext_routing_hdr_t)); + this->length=ROUTING_HEADER_MIN_LEN; + this->curr_addr=(u8 *)this->h.data; +} /* 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 *RoutingHeader::getBufferPointer(){ + return (u8*)(&this->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 RoutingHeader class is able to hold a maximum of + * sizeof(nping_icmpv6_hdr_t) bytes. If the supplied buffer is longer than + * that, only the first 1508 bytes will be stored in the internal buffer. + * @warning Supplied len MUST be at least 8 bytes (min ICMPv6 header length). + * @return OP_SUCCESS on success and OP_FAILURE in case of error */ +int RoutingHeader::storeRecvData(const u8 *buf, size_t len){ + if(buf==NULL || len<ROUTING_HEADER_MIN_LEN){ + this->length=0; + return OP_FAILURE; + }else{ + /* Store the first 4 bytes, so we can access length and routing type */ + memcpy(&(this->h), buf, 4); + + /* Our behaviour is different depending on the routing type. */ + switch(this->h.type){ + + // No checks against ROUTING_HEADER_MAX_LEN because h.len cannot get that large: + // h.len is u8, max value 0xff, so (0xff+1)*8 = 0x800 + // but ROUTING_HEADER_MAX_LEN is 8+256*8 = 0x808 + + /* Routing Type 0 (deprecated by RFC 5095)*/ + case 0: + /* Type 0 has a variable length, but the value of its HdrExtLen + * field must be even (because it must be a multiple of the + * IPv6 address size). We also make sure that the received buffer + * has as many bytes as the HdrExtLen field says it has, and + * that it doesn't exceed the maximum number of octets we + * can store in this object. */ + if(this->h.len%2==1 || ((unsigned int)(this->h.len+1))*8 > len){ + this->length=0; + return OP_FAILURE; + }else{ + int pkt_len=(this->h.len+1)*8; + this->reset(); + this->length=pkt_len; + memcpy(&(this->h), buf, this->length); + return OP_SUCCESS; + } + break; + + /* Routing Type 2 (For IPv6 Mobility. See RFC 6275) */ + case 2: + /* Type 2 has a fixed length. If we have that many octets, store + * them. We'll perform validation later in validate(). */ + if(len<ROUTING_TYPE_2_HEADER_LEN){ + this->length=0; + return OP_FAILURE; + }else{ + this->reset(); + memcpy(&(this->h), buf, ROUTING_TYPE_2_HEADER_LEN); + this->length=ROUTING_TYPE_2_HEADER_LEN; + return OP_SUCCESS; + } + break; + + /* Unknown routing type */ + default: + /* If this is some routing type that we don't know about, we'll have + * to store as much data as the header says it has. Obvioulsy, we + * check that we received as much data as the HdrExtLen advertises, + * and that we don't exceed our own internal limit. */ + if( ((unsigned int)(this->h.len+1))*8 > len){ + this->length=0; + return OP_FAILURE; + }else{ + this->reset(); + this->length=(this->h.len+1)*8; + memcpy(&(this->h), buf, this->length); + return OP_SUCCESS; + } + break; + } + } + return OP_FAILURE; +} /* 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 RoutingHeader::protocol_id() const { + return HEADER_TYPE_IPv6_ROUTE; +} /* 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. + * @return the length, in bytes, of the header, if its found to be valid or + * OP_FAILURE (-1) otherwise. */ +int RoutingHeader::validate(){ + + /* Check the object's length makes sense*/ + if(this->length < ROUTING_HEADER_MIN_LEN || this->length%8!=0) { + return OP_FAILURE; + } + + switch(this->h.type){ + /* Routing Type 0 (deprecated by RFC 5095)*/ + case 0: + /* Here we check that: + * 1) The length in HdrExtLen is even. + * 2) The length in HdrExtLen matches the octects stored in this object. + * 3) The length in HdrExtLen does not exceed our internal limit. */ + if(this->h.len%2==1 || (this->h.len+1)*8 != this->length){ + return OP_FAILURE; + } + + /* Also, for Type 0, the value in the SegmentsLeft field should be less + * than or equal to the number of addresses in the packet. We verify + * that using the value of the HDrExtLen field which, divided by two, + * yields the number of addresses in the packet. It certainly doesn't + * make sense for the packet to say there are 5 hops left when we + * have less than 5 IPv6 addresses. We allow it to be less than + * the number of addresses present in the packet because the RFC 2460 + * only talkes about segleft being greater than HDrExtLen/2, not less. */ + if(this->h.segleft > this->h.len/2){ + return OP_FAILURE; + } + break; + + /* Routing Type 2 (For IPv6 Mobility. See RFC 6275) */ + case 2: + /* Check that we have the exact number of octets we expect. */ + if(this->length!= ROUTING_TYPE_2_HEADER_LEN){ + return OP_FAILURE; + } + /* Also check that the HdrExtLen and SegmentsLeft fields have the + * value that RFC 6275 dictates. */ + if(this->h.segleft!=1 || this->h.len!=2){ + return OP_FAILURE; + } + break; + + /* Unknown routing type */ + default: + /* If this is some routing type that we don't know about, we just + * check that the length makes sense because we cannot make assumptions + * about the semantics of other fields. */ + if( this->length!=(this->h.len+1)*8){ + return OP_FAILURE; + } + break; + + } + 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 RoutingHeader::print(FILE *output, int detail) const { + fprintf(output, "Routing[nh=%d len=%d type=%d segleft=%d]", this->h.nh, this->h.len, this->h.type, this->h.segleft); + // TODO: @todo : Implement this + if(this->next!=NULL){ + print_separator(output, detail); + next->print(output, detail); + } + return OP_SUCCESS; +} /* End of print() */ + + +/******************************************************************************/ +/* PROTOCOL-SPECIFIC METHODS */ +/******************************************************************************/ + +/** Set Next Header field */ +int RoutingHeader::setNextHeader(u8 val){ + this->h.nh = val; + return OP_SUCCESS; +} /* End of setNextHeader() */ + + +/** Returns next header id */ +u8 RoutingHeader::getNextHeader(){ + return this->h.nh; +} /* End of getNextHeader() */ + + +/** Set routing type */ +int RoutingHeader::setRoutingType(u8 val){ + this->h.type = val; + return OP_SUCCESS; +} /* End of setRoutingType() */ + + +/** Returns the routing type */ +u8 RoutingHeader::getRoutingType(){ + return this->h.type; +} /* End of getRoutingType() */ + + +/** Set number of segments left */ +int RoutingHeader::setSegmentsLeft(u8 val){ + this->h.segleft = val; + return OP_SUCCESS; +} /* End of setSegmentsLeft() */ + + +/** Returns the number of segments left */ +u8 RoutingHeader::getSegmentsLeft(){ + return this->h.segleft; +} /* End of getSegmentsLeft() */ + + +/** Set number of segments left */ +int RoutingHeader::addAddress(struct in6_addr val){ + /* Check we don't exceed max length */ + if((this->length + 16)>ROUTING_HEADER_MAX_LEN) + return OP_FAILURE; + memcpy(this->curr_addr, val.s6_addr, 16); + this->curr_addr+=16; + this->h.len+=2; + this->length+=16; + return OP_SUCCESS; +} /* End of setSegmentsLeft() */ diff --git a/libnetutil/RoutingHeader.h b/libnetutil/RoutingHeader.h new file mode 100644 index 0000000..ebfdd7d --- /dev/null +++ b/libnetutil/RoutingHeader.h @@ -0,0 +1,176 @@ +/*************************************************************************** + * RoutingHeader.h -- The RoutingHeader Class represents an IPv6 Routing * + * extension header. * + ***********************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. */ + +#ifndef __ROUTING_HEADER_H__ +#define __ROUTING_HEADER_H__ 1 + +#include "IPv6ExtensionHeader.h" + +#define ROUTING_HEADER_MIN_LEN 8 +#define ROUTING_HEADER_MAX_LEN (8 + 256*8) +#define ROUTING_MAX_DATA_LEN 256*8 +#define ROUTING_TYPE_2_HEADER_LEN 24 +#define ROUTING_TYPE_0_MIN_LEN 8 + +class RoutingHeader : public IPv6ExtensionHeader { + + private: + + /* + 1) Generic Routing Header: + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Next Header | Hdr Ext Len | Routing Type | Segments Left | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + . . + . type-specific data . + . . + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + 2) Type 0 Routing header: + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Next Header | Hdr Ext Len | Routing Type=0| Segments Left | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + + + | | + + Address[1] + + | | + + + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + + + | | + + Address[2] + + | | + + + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + . . . + . . . + . . . + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + + + | | + + Address[n] + + | | + + + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + 3) Type 2 Routing header: + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Next Header | Hdr Ext Len=2 | Routing Type=2|Segments Left=1| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + + + | | + + Home Address + + | | + + + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + struct nping_ipv6_ext_routing_hdr{ + u8 nh; + u8 len; + u8 type; + u8 segleft; + u32 reserved; + u8 data[ROUTING_MAX_DATA_LEN]; + }__attribute__((__packed__)); + typedef struct nping_ipv6_ext_routing_hdr nping_ipv6_ext_routing_hdr_t; + + nping_ipv6_ext_routing_hdr_t h; + u8 *curr_addr; + + public: + RoutingHeader(); + ~RoutingHeader(); + void reset(); + u8 *getBufferPointer(); + int storeRecvData(const u8 *buf, size_t len); + int protocol_id() const; + int validate(); + int print(FILE *output, int detail) const; + + /* Protocol specific methods */ + int setNextHeader(u8 val); + u8 getNextHeader(); + + int setRoutingType(u8 val); + u8 getRoutingType(); + + int setSegmentsLeft(u8 val); + u8 getSegmentsLeft(); + + int addAddress(struct in6_addr val); + +}; /* End of class RoutingHeader */ + +#endif 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() */ + + diff --git a/libnetutil/TCPHeader.h b/libnetutil/TCPHeader.h new file mode 100644 index 0000000..6178be2 --- /dev/null +++ b/libnetutil/TCPHeader.h @@ -0,0 +1,259 @@ +/*************************************************************************** + * TCPHeader.h -- 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. */ + +#ifndef __TCPHEADER_H__ +#define __TCPHEADER_H__ 1 + +#include "TransportLayerElement.h" + +/* TCP FLAGS */ +#define TH_FIN 0x01 +#define TH_SYN 0x02 +#define TH_RST 0x04 +#define TH_PSH 0x08 +#define TH_ACK 0x10 +#define TH_URG 0x20 +#define TH_ECN 0x40 +#define TH_CWR 0x80 + +/* TCP OPTIONS */ +#define TCPOPT_EOL 0 /* End of Option List (RFC793) */ +#define TCPOPT_NOOP 1 /* No-Operation (RFC793) */ +#define TCPOPT_MSS 2 /* Maximum Segment Size (RFC793) */ +#define TCPOPT_WSCALE 3 /* WSOPT - Window Scale (RFC1323) */ +#define TCPOPT_SACKOK 4 /* SACK Permitted (RFC2018) */ +#define TCPOPT_SACK 5 /* SACK (RFC2018) */ +#define TCPOPT_ECHOREQ 6 /* Echo (obsolete) (RFC1072)(RFC6247) */ +#define TCPOPT_ECHOREP 7 /* Echo Reply (obsolete) (RFC1072)(RFC6247) */ +#define TCPOPT_TSTAMP 8 /* TSOPT - Time Stamp Option (RFC1323) */ +#define TCPOPT_POCP 9 /* Partial Order Connection Permitted (obsol.) */ +#define TCPOPT_POSP 10 /* Partial Order Service Profile (obsolete) */ +#define TCPOPT_CC 11 /* CC (obsolete) (RFC1644)(RFC6247) */ +#define TCPOPT_CCNEW 12 /* CC.NEW (obsolete) (RFC1644)(RFC6247) */ +#define TCPOPT_CCECHO 13 /* CC.ECHO (obsolete) (RFC1644)(RFC6247) */ +#define TCPOPT_ALTCSUMREQ 14 /* TCP Alternate Checksum Request (obsolete) */ +#define TCPOPT_ALTCSUMDATA 15 /* TCP Alternate Checksum Data (obsolete) */ +#define TCPOPT_MD5 19 /* MD5 Signature Option (obsolete) (RFC2385) */ +#define TCPOPT_SCPS 20 /* SCPS Capabilities */ +#define TCPOPT_SNACK 21 /* Selective Negative Acknowledgements */ +#define TCPOPT_QSRES 27 /* Quick-Start Response (RFC4782) */ +#define TCPOPT_UTO 28 /* User Timeout Option (RFC5482) */ +#define TCPOPT_AO 29 /* TCP Authentication Option (RFC5925) */ + +/* Internal constants */ +#define TCP_HEADER_LEN 20 +#define MAX_TCP_OPTIONS_LEN 40 +#define MAX_TCP_PAYLOAD_LEN 65495 /**< Max len of a TCP packet */ + +/* Default header values */ +#define TCP_DEFAULT_SPORT 20 +#define TCP_DEFAULT_DPORT 80 +#define TCP_DEFAULT_SEQ 0 +#define TCP_DEFAULT_ACK 0 +#define TCP_DEFAULT_FLAGS 0x02 +#define TCP_DEFAULT_WIN 8192 +#define TCP_DEFAULT_URP 0 + + + +/* ++--------+--------+---------+--------... +| Type | Len | Value ++--------+--------+---------+--------... +*/ +struct nping_tcp_opt { + u8 type; /* Option type code. */ + u8 len; /* Option length. */ + u8 *value; /* Option value */ +}__attribute__((__packed__)); +typedef struct nping_tcp_opt nping_tcp_opt_t; + + +class TCPHeader : public TransportLayerElement { + + private: + /* + 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 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Offset| Res. |C|E|U|A|P|R|S|F| Window | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Checksum | Urgent Pointer | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Options | Padding | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + struct nping_tcp_hdr { + u16 th_sport; /* Source port */ + u16 th_dport; /* Destination port */ + u32 th_seq; /* Sequence number */ + u32 th_ack; /* Acknowledgement number */ + #if WORDS_BIGENDIAN + u8 th_off:4; /* Data offset */ + u8 th_x2:4; /* Reserved */ + #else + u8 th_x2:4; /* Reserved */ + u8 th_off:4; /* Data offset */ + #endif + u8 th_flags; /* Flags */ + u16 th_win; /* Window size */ + u16 th_sum; /* Checksum */ + u16 th_urp; /* Urgent pointer */ + + u8 options[MAX_TCP_OPTIONS_LEN ]; /* Space for TCP Options */ + }__attribute__((__packed__)); + + typedef struct nping_tcp_hdr nping_tcp_hdr_t; + + nping_tcp_hdr_t h; + + int tcpoptlen; /**< Length of TCP options */ + + void __tcppacketoptinfo(const u8 *optp, int len, char *result, int bufsize) const; + + public: + + TCPHeader(); + ~TCPHeader(); + void reset(); + u8 *getBufferPointer(); + int storeRecvData(const u8 *buf, size_t len); + int protocol_id() const; + int validate(); + int print(FILE *output, int detail) const; + + int setSourcePort(u16 p); + u16 getSourcePort() const; + + int setDestinationPort(u16 p); + u16 getDestinationPort() const; + + int setSeq(u32 p); + u32 getSeq() const; + + int setAck(u32 p); + u32 getAck() const; + + int setOffset(u8 o); + int setOffset(); + u8 getOffset() const; + + int setReserved(u8 r); + u8 getReserved() const; + + int setFlags(u8 f); + u8 getFlags() const; + u16 getFlags16() const; + bool setCWR(); + bool unsetCWR(); + bool getCWR() const; + bool setECE(); + bool unsetECE(); + bool getECE() const; + bool setECN(); + bool unsetECN(); + bool getECN() const; + bool setURG(); + bool unsetURG(); + bool getURG() const; + bool setACK(); + bool unsetACK(); + bool getACK() const; + bool setPSH(); + bool unsetPSH(); + bool getPSH() const; + bool setRST(); + bool unsetRST(); + bool getRST() const; + bool setSYN(); + bool unsetSYN(); + bool getSYN() const; + bool setFIN(); + bool unsetFIN(); + bool getFIN() const; + + int setWindow(u16 p); + u16 getWindow() const; + + int setUrgPointer(u16 l); + u16 getUrgPointer() const; + + int setSum(u16 s); + int setSum(struct in_addr source, struct in_addr destination); + int setSum(); + int setSumRandom(); + int setSumRandom(struct in_addr source, struct in_addr destination); + u16 getSum() const; + + int setOptions(const u8 *optsbuff, size_t optslen); + const u8 *getOptions(size_t *optslen) const; + nping_tcp_opt_t getOption(unsigned int index) const; + static const char *optcode2str(u8 optcode); + +}; /* End of class TCPHeader */ + +#endif /* __TCPHEADER_H__ */ diff --git a/libnetutil/TransportLayerElement.cc b/libnetutil/TransportLayerElement.cc new file mode 100644 index 0000000..cdbbb9e --- /dev/null +++ b/libnetutil/TransportLayerElement.cc @@ -0,0 +1,126 @@ +/*************************************************************************** + * TransportLayerElement.cc -- Class TransportLayerElement is a generic * + * class that represents a transport layer protocol header. Classes like * + * TCPHeader or UDPHeader inherit from it. * + * * + ***********************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 "TransportLayerElement.h" +#include "IPv4Header.h" +#include "IPv6Header.h" + + +/** Computes and returns the Internet checksum. + * @warning This method requires the object to be linked to either an IPv6Header + * object or an IPv4Header one, so the caller must ensure that objects are + * properly linked with calls to setNextElement() like this: + * + * IPv6Header ip6; + * TCPHeader tcp; + * [...] # Set header fields + * ip6.setNextElement(&tcp); + * tcp.setSum(); + * + * Note that there can be a number of other headers (like IPv6 extension headers) + * between the transport header and the network one, but all of them need to + * be linked in order for this method to traverse the list of headers and find + * the IP source and destination address, required to compute the checksum. So + * things like the following are OK: + * + * IPv6Header ip6; + * HopByHopHeader hop; + * RoutingHeader rte; + * FragmentHeader frg; + * UDPHeader udp; + * [...] # Set whatever header fields you need + * ip6.setNextElement(&hop); + * hop.setNextElement(&rte); + * rte.setNextElement(&frg); + * frg.setNextElement(&udp); + * udp.setSum(); # setSum() will be able to reach the IPv6Header. */ +u16 TransportLayerElement::compute_checksum(){ + PacketElement *hdr; + hdr=this->getPrevElement(); + u16 final_sum=0; + /* Traverse the list of headers backwards until we find an IP header */ + while(hdr!=NULL){ + if (hdr->protocol_id()==HEADER_TYPE_IPv6){ + IPv6Header *v6hdr=(IPv6Header *)hdr; + struct in6_addr i6src, i6dst; + memcpy(i6src.s6_addr, v6hdr->getSourceAddress(), 16); + memcpy(i6dst.s6_addr, v6hdr->getDestinationAddress(), 16); + u8 *buff=(u8 *)safe_malloc(this->getLen()); + this->dumpToBinaryBuffer(buff, this->getLen()); + final_sum=ipv6_pseudoheader_cksum(&i6src, &i6dst, this->protocol_id(), this->getLen(), buff); + free(buff); + return final_sum; + }else if(hdr->protocol_id()==HEADER_TYPE_IPv4){ + IPv4Header *v4hdr=(IPv4Header *)hdr; + struct in_addr i4src, i4dst; + memcpy(&(i4src.s_addr), v4hdr->getSourceAddress(), 4); + memcpy(&(i4dst.s_addr), v4hdr->getDestinationAddress(), 4); + u8 *buff=(u8 *)safe_malloc(this->getLen()); + this->dumpToBinaryBuffer(buff, this->getLen()); + final_sum=ipv4_pseudoheader_cksum(&i4src, &i4dst, this->protocol_id(), this->getLen(), buff); + free(buff); + return final_sum; + }else{ + hdr=hdr->getPrevElement(); + } + } + return 0; +} /* End of setSum() */ diff --git a/libnetutil/TransportLayerElement.h b/libnetutil/TransportLayerElement.h new file mode 100644 index 0000000..2496af1 --- /dev/null +++ b/libnetutil/TransportLayerElement.h @@ -0,0 +1,91 @@ +/*************************************************************************** + * TransportLayerElement.cc -- Class TransportLayerElement is a generic * + * class that represents a transport layer protocol header. Classes like * + * TCPHeader or UDPHeader inherit from it. * + * * + ***********************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. */ + +#ifndef TRANSPORTLAYERELEMENT_H +#define TRANSPORTLAYERELEMENT_H 1 + +#include "PacketElement.h" + +/// class TransportLayerElement - +class TransportLayerElement : public PacketElement { + + public: + + /* Returns source port. */ + virtual u16 getSourcePort() const = 0; + + /* Sets source port. */ + virtual int setSourcePort(u16 val) = 0; + + /* Returns destination port. */ + virtual u16 getDestinationPort() const = 0; + + /* Sets destination port. */ + virtual int setDestinationPort(u16 val) = 0; + + /* Sets checksum. */ + virtual int setSum(u16 val) = 0; + + protected: + u16 compute_checksum(); +}; + +#endif diff --git a/libnetutil/UDPHeader.cc b/libnetutil/UDPHeader.cc new file mode 100644 index 0000000..3c86d68 --- /dev/null +++ b/libnetutil/UDPHeader.cc @@ -0,0 +1,298 @@ +/*************************************************************************** + * UDPHeader.cc -- The UDPHeader Class represents a UDP 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 "UDPHeader.h" + +/******************************************************************************/ +/* CONTRUCTORS, DESTRUCTORS AND INITIALIZATION METHODS */ +/******************************************************************************/ +UDPHeader::UDPHeader(){ + this->reset(); +} /* End of UDPHeader constructor */ + + +UDPHeader::~UDPHeader(){ + +} /* End of UDPHeader destructor */ + + +/** Sets every attribute to its default value */ +void UDPHeader::reset(){ + this->length=UDP_HEADER_LEN; + this->setSourcePort(UDP_DEFAULT_SPORT); + this->setDestinationPort(UDP_DEFAULT_DPORT); + this->setTotalLength(UDP_HEADER_LEN); + this->setSum(0); +} /* 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 * UDPHeader::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 UDPHeader class is able to hold a maximum of 8 bytes. If the + * supplied buffer is longer than that, only the first 8 bytes will be stored + * in the internal buffer. + * @warning Supplied len MUST be at least 8 bytes (UDP header length). + * @return OP_SUCCESS on success and OP_FAILURE in case of error */ +int UDPHeader::storeRecvData(const u8 *buf, size_t len){ + if(buf==NULL || len<UDP_HEADER_LEN){ + return OP_FAILURE; + }else{ + this->reset(); /* Re-init the object, just in case the caller had used it already */ + this->length=UDP_HEADER_LEN; + memcpy(&(this->h), buf, UDP_HEADER_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 UDPHeader::protocol_id() const { + return HEADER_TYPE_UDP; +} /* 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. + * @return the length, in bytes, of the header, if its found to be valid or + * OP_FAILURE (-1) otherwise. */ +int UDPHeader::validate(){ + if( this->length!=UDP_HEADER_LEN) + return OP_FAILURE; + else + return UDP_HEADER_LEN; +} /* 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 UDPHeader::print(FILE *output, int detail) const { + fprintf(output, "UDP["); + fprintf(output, "%d", this->getSourcePort()); + fprintf(output, " >"); + fprintf(output, " %d", this->getDestinationPort()); + if(detail>=PRINT_DETAIL_HIGH) + fprintf(output, " len=%d", (int)this->getTotalLength() ); + if(detail>=PRINT_DETAIL_MED) + fprintf(output, " csum=0x%04X", ntohs( this->getSum() )); + fprintf(output, "]"); + if(this->next!=NULL){ + print_separator(output, detail); + next->print(output, detail); + } + return OP_SUCCESS; +} /* End of print() */ + + +/******************************************************************************/ +/* PROTOCOL-SPECIFIC METHODS */ +/******************************************************************************/ + +/** Sets source port. + * @warning Port must be supplied in host byte order. This method performs + * byte order conversion using htons() */ +int UDPHeader::setSourcePort(u16 p){ + h.uh_sport = htons(p); + return OP_SUCCESS; +} /* End of setSrcPort() */ + + +/** Returns source port in HOST byte order */ +u16 UDPHeader::getSourcePort() const { + return ntohs(h.uh_sport); +} /* End of getSrcPort() */ + + +/** Sets destination port. + * @warning Port must be supplied in host byte order. This method performs + * byte order conversion using htons() */ +int UDPHeader::setDestinationPort(u16 p){ + h.uh_dport = htons(p); + return OP_SUCCESS; +} /* End of setDstPort() */ + + +/** Returns destination port in HOST byte order */ +u16 UDPHeader::getDestinationPort() const { + return ntohs(h.uh_dport); +} /* End of getDstPort() */ + + +int UDPHeader::setSum(struct in_addr src, struct in_addr dst){ + int bufflen; + u8 aux[ 65535-8 ]; + /* FROM: RFC 5405 Unicast UDP Usage Guidelines, November 2008 + * "A UDP datagram is carried in a single IP packet and is hence limited to + * a maximum payload of 65,507 bytes for IPv4 and 65,527 bytes for IPv6" + * + * So, UDP is supposed to be able to carry 65535-8 bytes but in fact it can + * only carry 65,507 or 65,527. However, we are not taking that into account + * here because UDP is supposed to be independent of IPv4, IPv6 or + * whatever other network layer protocol is used to carry the UDP datagrams.*/ + h.uh_sum = 0; + + /* Copy packet contents to a buffer */ + bufflen=dumpToBinaryBuffer(aux, 65536-8 ); + + /* Compute checksum */ + h.uh_sum = ipv4_pseudoheader_cksum(&src, &dst, IPPROTO_UDP,bufflen, (char *) aux); + + return OP_SUCCESS; +} /* End of setSum() */ + + +/** @warning Sum is set to supplied value with NO byte ordering conversion + * performed. */ +int UDPHeader::setSum(u16 s){ + h.uh_sum = s; + return OP_SUCCESS; +} /* End of setSum() */ + + +int UDPHeader::setSum(){ + this->h.uh_sum=0; + this->h.uh_sum = this->compute_checksum(); + return OP_SUCCESS; +} /* End of setSum() */ + + +/** Set the UDP checksum field to a random value, which may accidentally + * match the correct checksum */ +int UDPHeader::setSumRandom(){ + h.uh_sum=(1 + (get_random_u16()%(65535-1))); /* Discard value zero */ + return OP_SUCCESS; +} /* End of setSumRandom() */ + + +/** Set the UDP 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 UDPHeader::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.uh_sum=(1 + (get_random_u16()%(65535-1))))==correct_csum); + return OP_SUCCESS; +} /* End of setSumRandom() */ + + +u16 UDPHeader::getSum() const { + return h.uh_sum; +} /* End of getSum() */ + + +int UDPHeader::setTotalLength(){ + int mylen = 8; + int otherslen=0; + + if (next!=NULL) + otherslen=next->getLen(); + + /* FROM: RFC 5405 Unicast UDP Usage Guidelines, November 2008 + * "A UDP datagram is carried in a single IP packet and is hence limited to + * a maximum payload of 65,507 bytes for IPv4 and 65,527 bytes for IPv6" + * + * So, UDP is supposed to be able to carry 65535-8 bytes but in fact it can + * only carry 65,507 or 65,527. However, we are not taking that into account + * here because UDP is supposed to be independent of IPv4, IPv6 or + * whatever other network layer protocol is used to carry the UDP datagrams.*/ + if (otherslen < 0 || otherslen > 65535 || (mylen+otherslen) > 65535){ + printf("UDPHeader::setTotalLength(): Invalid length.\n"); + return OP_FAILURE; + } + + h.uh_ulen=htons( mylen+otherslen ); + + return OP_SUCCESS; +} /* End of setTotalLength() */ + + +/** @warning Supplied value MUST be in HOST byte order */ +int UDPHeader::setTotalLength(u16 l){ + this->h.uh_ulen=htons(l); + return OP_SUCCESS; +} /* End of setTotalLength() */ + + +/** @warning Returned value is in HOST byte order */ +u16 UDPHeader::getTotalLength() const { + return ntohs(this->h.uh_ulen); +} /* End of getTotalLength() */ + + diff --git a/libnetutil/UDPHeader.h b/libnetutil/UDPHeader.h new file mode 100644 index 0000000..970ef84 --- /dev/null +++ b/libnetutil/UDPHeader.h @@ -0,0 +1,127 @@ +/*************************************************************************** + * UDPHeader.h -- The UDPHeader Class represents a UDP 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. */ + +#ifndef UDPHEADER_H +#define UDPHEADER_H 1 + +#include "TransportLayerElement.h" + +#define UDP_HEADER_LEN 8 + +/* Default header values */ +#define UDP_DEFAULT_SPORT 53 +#define UDP_DEFAULT_DPORT 53 + +class UDPHeader : public TransportLayerElement { + + private: + /* + 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 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Length | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + struct nping_udp_hdr{ + u16 uh_sport; + u16 uh_dport; + u16 uh_ulen; + u16 uh_sum; + }__attribute__((__packed__)); + + typedef struct nping_udp_hdr nping_udp_hdr_t; + + nping_udp_hdr_t h; + + public: + + UDPHeader(); + ~UDPHeader(); + void reset(); + u8 *getBufferPointer(); + int storeRecvData(const u8 *buf, size_t len); + int protocol_id() const; + int validate(); + int print(FILE *output, int detail) const; + + int setSourcePort(u16 p); + u16 getSourcePort() const; + + int setDestinationPort(u16 p); + u16 getDestinationPort() const; + + int setTotalLength(); + int setTotalLength(u16 l); + u16 getTotalLength() const; + + int setSum(struct in_addr source, struct in_addr destination); + int setSum(u16 s); + int setSum(); + int setSumRandom(); + int setSumRandom(struct in_addr source, struct in_addr destination); + u16 getSum() const; + +}; /* End of class UDPHeader */ + + +#endif diff --git a/libnetutil/libnetutil.vcxproj b/libnetutil/libnetutil.vcxproj new file mode 100644 index 0000000..5decf6e --- /dev/null +++ b/libnetutil/libnetutil.vcxproj @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{99157C3F-39F6-4663-99D7-1D9C1484494E}</ProjectGuid>
+ <RootNamespace>libnetutil</RootNamespace>
+ <Keyword>Win32Proj</Keyword>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;../mswin32;../nbase;..\..\nmap-mswin32-aux\Npcap\Include;../libdnet-stripped/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;BPF_MAJOR_VERSION;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(OutDir)libnetutil.lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>..;../mswin32;../nbase;..\..\nmap-mswin32-aux\Npcap\Include;../libdnet-stripped/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;BPF_MAJOR_VERSION;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="ARPHeader.cc" />
+ <ClCompile Include="DestOptsHeader.cc" />
+ <ClCompile Include="EthernetHeader.cc" />
+ <ClCompile Include="FragmentHeader.cc" />
+ <ClCompile Include="HopByHopHeader.cc" />
+ <ClCompile Include="ICMPv4Header.cc" />
+ <ClCompile Include="ICMPv6Header.cc" />
+ <ClCompile Include="ICMPv6Option.cc" />
+ <ClCompile Include="ICMPv6RRBody.cc" />
+ <ClCompile Include="IPv4Header.cc" />
+ <ClCompile Include="IPv6Header.cc" />
+ <ClCompile Include="netutil.cc" />
+ <ClCompile Include="NetworkLayerElement.cc" />
+ <ClCompile Include="PacketElement.cc" />
+ <ClCompile Include="PacketParser.cc" />
+ <ClCompile Include="RawData.cc" />
+ <ClCompile Include="RoutingHeader.cc" />
+ <ClCompile Include="TCPHeader.cc" />
+ <ClCompile Include="TransportLayerElement.cc" />
+ <ClCompile Include="UDPHeader.cc" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="ApplicationLayerElement.h" />
+ <ClInclude Include="ARPHeader.h" />
+ <ClInclude Include="DataLinkLayerElement.h" />
+ <ClInclude Include="DestOptsHeader.h" />
+ <ClInclude Include="EthernetHeader.h" />
+ <ClInclude Include="FragmentHeader.h" />
+ <ClInclude Include="HopByHopHeader.h" />
+ <ClInclude Include="ICMPHeader.h" />
+ <ClInclude Include="ICMPv4Header.h" />
+ <ClInclude Include="ICMPv6Header.h" />
+ <ClInclude Include="ICMPv6Option.h" />
+ <ClInclude Include="ICMPv6RRBody.h" />
+ <ClInclude Include="IPv4Header.h" />
+ <ClInclude Include="IPv6Header.h" />
+ <ClInclude Include="netutil.h" />
+ <ClInclude Include="NetworkLayerElement.h" />
+ <ClInclude Include="npacket.h" />
+ <ClInclude Include="PacketElement.h" />
+ <ClInclude Include="PacketParser.h" />
+ <ClInclude Include="RawData.h" />
+ <ClInclude Include="RoutingHeader.h" />
+ <ClInclude Include="TCPHeader.h" />
+ <ClInclude Include="TransportLayerElement.h" />
+ <ClInclude Include="UDPHeader.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file diff --git a/libnetutil/netutil.cc b/libnetutil/netutil.cc new file mode 100644 index 0000000..ff7279e --- /dev/null +++ b/libnetutil/netutil.cc @@ -0,0 +1,4777 @@ +/*************************************************************************** + * netutil.cc * + * * + ***********************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/ + * + ***************************************************************************/ + +/* Since OS X 10.7, we must declare whether we expect RFC 2292 or RFC 3542 + behavior from <netinet6/in6.h>. */ +#define __APPLE_USE_RFC_3542 + +#if HAVE_CONFIG_H +#include "../nmap_config.h" +#endif + +#include "nbase.h" + +#ifndef WIN32 +#include <sys/uio.h> +#include <sys/ioctl.h> +#endif + +#include <assert.h> +#include <errno.h> +#include <sys/types.h> +#if HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#if HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> /* SIOCGIFCONF for Solaris */ +#endif + +/* Define CMSG_* symbols for Solaris 9 and earlier. See + http://wiki.opencsw.org/porting-faq#toc10. */ +#if defined(__sun) || defined(__sun__) +# ifndef CMSG_ALIGN +# ifdef __sun__ +# define CMSG_ALIGN(len) _CMSG_DATA_ALIGN (len) +# else + /* aligning to sizeof (long) is assumed to be portable (fd.o#40235) */ +# define CMSG_ALIGN(len) (((len) + sizeof (long) - 1) & ~(sizeof (long) - 1)) +# endif +# endif +# ifndef CMSG_SPACE +# define CMSG_SPACE(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + CMSG_ALIGN (len)) +# endif +# ifndef CMSG_LEN +# define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) +# endif +#endif /* Solaris */ + +#ifdef WIN32 +typedef unsigned __int32 u_int32_t; +typedef unsigned __int16 u_int16_t; +typedef unsigned __int8 u_int8_t; +#endif + +#if HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_LINUX_RTNETLINK_H +#include <linux/rtnetlink.h> +#endif + +#ifndef NETINET_IN_SYSTM_H /* This guarding is needed for at least some versions of OpenBSD */ +#include <netinet/in_systm.h> +#define NETINET_IN_SYSTM_H +#endif + +#include "netutil.h" + +#if HAVE_NET_IF_H +#ifndef NET_IF_H /* This guarding is needed for at least some versions of OpenBSD */ +#include <net/if.h> +#define NET_IF_H +#endif +#endif +#ifndef NETINET_IP_H /* This guarding is needed for at least some versions of OpenBSD */ +#include <netinet/ip.h> +#define NETINET_IP_H +#endif +#include <net/if_arp.h> + +#if HAVE_SYS_RESOURCE_H +#include <sys/resource.h> +#endif + +#define NBASE_MAX_ERR_STR_LEN 1024 /* Max length of an error message */ + +#ifndef PCAP_NETMASK_UNKNOWN +/* libpcap before 1.1.1 (e.g. WinPcap) doesn't handle this specially, so just use 0 netmask */ +#define PCAP_NETMASK_UNKNOWN 0 +#endif + +/** Print fatal error messages to stderr and then exits. A newline + character is printed automatically after the supplied text. + * @warning This function does not return because it calls exit() */ +void netutil_fatal(const char *str, ...){ + va_list list; + char errstr[NBASE_MAX_ERR_STR_LEN]; + memset(errstr,0, NBASE_MAX_ERR_STR_LEN); + + va_start(list, str); + + fflush(stdout); + + /* Print error msg to strerr */ + vfprintf(stderr, str, list); + fprintf(stderr,"\n"); + va_end(list); + + exit(EXIT_FAILURE); +} /* End of fatal() */ + +/** Print error messages to stderr and then return. A newline + character is printed automatically after the supplied text.*/ +int netutil_error(const char *str, ...){ + va_list list; + char errstr[NBASE_MAX_ERR_STR_LEN]; + memset(errstr,0, NBASE_MAX_ERR_STR_LEN); + + va_start(list, str); + + fflush(stdout); + + /* Print error msg to strerr */ + vfprintf(stderr, str, list); + fprintf(stderr,"\n"); + va_end(list); + + return 0; + +} /* End of error() */ + +/* This function converts zero-terminated 'txt' string to binary 'data'. + It is used to parse user input for ip options. Some examples of possible input + strings and results: + '\x01*2\xA2' -> [0x01,0x01,0xA2] // with 'x' number is parsed in hex + '\01\01\255' -> [0x01,0x01,0xFF] // without 'x' its in decimal + '\x01\x00*2' -> [0x01,0x00,0x00] // '*' is copying char + 'R' -> Record Route with 9 slots + 'S 192.168.0.1 172.16.0.1' -> Strict Route with 2 slots + 'L 192.168.0.1 172.16.0.1' -> Loose Route with 2 slots + 'T' -> Record Timestamp with 9 slots + 'U' -> Record Timestamp and Ip Address with 4 slots + On success, the function returns the length of the final binary + options stored in "data". In case of error, OP_FAILURE is returned + and the "errstr" buffer is filled with an error message + (unless it's NULL). Note that the returned error message does NOT + contain a newline character at the end. */ +int parse_ip_options(const char *txt, u8 *data, int datalen, int* firsthopoff, int* lasthopoff, char *errstr, size_t errstrlen){ + enum{ + NONE = 0, + SLASH = 1, + MUL = 2, + RR = 3, + TIME = 4, + } s = NONE; + char *n, lc; + const char *c = txt; + u8 *d = data; + int i,j; + int base = 10; + u8 *dataend = &data[datalen]; + u8 *len = NULL; + char buf[32]; + memset(data, 0, datalen); + int sourcerouting = 0; + long strtolbyte = 0; // used to check strtol() return boundaries + + for(;*c;c++){ + switch(s){ + case SLASH: + // parse \x00 string + if(*c == 'x'){// just ignore this char + base = 16; + break; + } + if(isxdigit(*c)){ + strtolbyte = strtol(c, &n, base); + if((strtolbyte < 0) || (strtolbyte > 255)){ + if(errstr) Snprintf(errstr, errstrlen, "invalid ipv4 address format"); + return OP_FAILURE; + } + *d++ = (u8) strtolbyte; + c = n-1; + }else{ + if(errstr) Snprintf(errstr, errstrlen, "not a digit after '\\'"); + return OP_FAILURE; + } + s = NONE; + break; + case MUL: + if(d==data){ + if(errstr) Snprintf(errstr, errstrlen, "nothing before '*' char"); + return OP_FAILURE; + } + i = strtol(c, &n, 10); + if(i<2){ + if(errstr) Snprintf(errstr, errstrlen, "bad number after '*'"); + return OP_FAILURE; + } + c = n-1; // move current txt pointer + lc = *(d-1); // last char, we'll copy this + for(j=1; j<i; j++){ + *d++ = lc; + if(d == dataend) // check for overflow + goto after; + } + s = NONE; + break; + case RR: + if(*c==' ' || *c==',') + break; + n = buf; + while((*c=='.' || (*c>='0' && *c<='9')) && n-buf <= ((int)sizeof(buf)-1)) + *n++ = *c++; + *n = '\0'; c--; + if(d+4>=dataend){ + if(errstr) Snprintf(errstr, errstrlen, "Buffer too small. Or input data too big :)"); + return OP_FAILURE; + } + i = inet_pton(AF_INET, buf, d); + if(i<1){ + if(errstr) Snprintf(errstr, errstrlen, "Not a valid ipv4 address '%s'",buf); + return OP_FAILURE; + } + // remember offset of first hop + if(sourcerouting && !*firsthopoff) + *firsthopoff = d - data; + d+=4; + if(*len<37) + *len += 4; + break; + case TIME: + if(errstr) Snprintf(errstr, errstrlen, "No more arguments allowed!"); + return OP_FAILURE; + default: + switch(*c){ + case '\\':s = SLASH;base=10;break; + case '*':s = MUL;break; + case 'R': + case 'S': + case 'L': + if(d != data){ + if(errstr) Snprintf(errstr, errstrlen, "This option can't be used in that way"); + return OP_FAILURE; + } + *d++ = '\x01';//NOP + switch(*c){ + case 'R':*d++ = 7;break; + case 'S':*d++ = 137; sourcerouting=1; break; + case 'L':*d++ = 131; sourcerouting=1; break; + } + len = d; + *d++ = (*c=='R')? 39 : 3; // length: 3+4*9 bytes + *d++ = 4; //pointer + s = RR; + break; + case 'T': + case 'U': + if(d != data){ + if(errstr) Snprintf(errstr, errstrlen, "This option can't be used in that way"); + return OP_FAILURE; + } + *d++ = 68; // option type + len = d; + *d++ = (*c=='U') ? 36 : 40; // length: 3+4*9 bytes or 4+4*9 bytes + *d++ = 5; // pointer + *d++ = (*c=='U') ? 1 : 0; // flag: address and Time fields + s = TIME; + break; + default://*d++ = *c; + if(errstr) Snprintf(errstr, errstrlen, "Bad character in ip option '%c'",*c); + return OP_FAILURE; + } + } + if(d == dataend) + break; + assert(d<dataend); + } + if(sourcerouting){ + if(*len<37){ + *len+=4; + *lasthopoff = d - data; + *d++ = 0;*d++ = 0;*d++ = 0;*d++ = 0; + }else{ + if(errstr) Snprintf(errstr, errstrlen, "When using source routing you must leave at least one slot for target's ip."); + return OP_FAILURE; + } + } + if(s == RR) + return(*len+1); // because we inject NOP before + if(s == TIME) + return(*len); +after: + return(d - data); +} + +/* Internal helper for resolve and resolve_numeric. addl_flags is ored into + hints.ai_flags, so you can add AI_NUMERICHOST. */ +static int resolve_internal(const char *hostname, unsigned short port, + struct sockaddr_storage *ss, size_t *sslen, int af, int addl_flags) { + struct addrinfo hints; + struct addrinfo *result; + char portbuf[16]; + char *servname = NULL; + int rc; + + assert(hostname); + assert(ss); + assert(sslen); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = af; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags |= addl_flags; + + /* Make the port number a string to give to getaddrinfo. */ + if (port != 0) { + rc = Snprintf(portbuf, sizeof(portbuf), "%hu", port); + assert(rc >= 0 && (size_t) rc < sizeof(portbuf)); + servname = portbuf; + } + + rc = getaddrinfo(hostname, servname, &hints, &result); + if (rc != 0) + return rc; + if (result == NULL) + return EAI_NONAME; + assert(result->ai_addrlen > 0 && result->ai_addrlen <= (int) sizeof(struct sockaddr_storage)); + *sslen = result->ai_addrlen; + memcpy(ss, result->ai_addr, *sslen); + freeaddrinfo(result); + + return 0; +} + +/* Resolves the given hostname or IP address with getaddrinfo, and stores the + first result (if any) in *ss and *sslen. The value of port will be set in the + appropriate place in *ss; set to 0 if you don't care. af may be AF_UNSPEC, in + which case getaddrinfo may return e.g. both IPv4 and IPv6 results; which one + is first depends on the system configuration. Returns 0 on success, or a + getaddrinfo return code (suitable for passing to gai_strerror) on failure. + *ss and *sslen are always defined when this function returns 0. */ +int resolve(const char *hostname, unsigned short port, + struct sockaddr_storage *ss, size_t *sslen, int af) { + return resolve_internal(hostname, port, ss, sslen, af, 0); +} + +/* As resolve, but do not do DNS resolution of hostnames; the first argument + must be the string representation of a numeric IP address. */ +int resolve_numeric(const char *ip, unsigned short port, + struct sockaddr_storage *ss, size_t *sslen, int af) { + return resolve_internal(ip, port, ss, sslen, af, AI_NUMERICHOST); +} + +/* + * Returns 1 if this is a reserved IP address, where "reserved" means + * either a private address, non-routable address, or even a non-reserved + * but unassigned address which has an extremely high probability of being + * black-holed. + * + * We try to optimize speed when ordering the tests. This optimization + * assumes that all byte values are equally likely in the input. + * + * Check + * <http://www.iana.org/assignments/ipv4-address-space/ipv4-address-space.txt> + * for the most recent assigments and + * <http://www.cymru.com/Documents/bogon-bn-nonagg.txt> for bogon + * netblocks. + */ +int ip_is_reserved(struct in_addr *ip) +{ + char *ipc = (char *) &(ip->s_addr); + unsigned char i1 = ipc[0], i2 = ipc[1], i3 = ipc[2]; /* i4 not currently used - , i4 = ipc[3]; */ + + /* do all the /7's and /8's with a big switch statement, hopefully the + * compiler will be able to optimize this a little better using a jump table + * or what have you + */ + switch (i1) + { + case 0: /* 000/8 is IANA reserved */ + case 10: /* the infamous 10.0.0.0/8 */ + case 127: /* 127/8 is reserved for loopback */ + return 1; + default: + break; + } + + /* 172.16.0.0/12 is reserved for private nets by RFC1918 */ + if (i1 == 172 && i2 >= 16 && i2 <= 31) + return 1; + + /* 192.0.2.0/24 is reserved for documentation and examples (RFC5737) */ + /* 192.88.99.0/24 is used as 6to4 Relay anycast prefix by RFC3068 */ + /* 192.168.0.0/16 is reserved for private nets by RFC1918 */ + if (i1 == 192) { + if (i2 == 0 && i3 == 2) + return 1; + if (i2 == 88 && i3 == 99) + return 1; + if (i2 == 168) + return 1; + } + + /* 198.18.0.0/15 is used for benchmark tests by RFC2544 */ + /* 198.51.100.0/24 is reserved for documentation (RFC5737) */ + if (i1 == 198) { + if (i2 == 18 || i2 == 19) + return 1; + if (i2 == 51 && i3 == 100) + return 1; + } + + /* 169.254.0.0/16 is reserved for DHCP clients seeking addresses - RFC3927 */ + if (i1 == 169 && i2 == 254) + return 1; + + /* 203.0.113.0/24 is reserved for documentation (RFC5737) */ + if (i1 == 203 && i2 == 0 && i3 == 113) + return 1; + + /* 224-239/8 is all multicast stuff */ + /* 240-255/8 is IANA reserved */ + if (i1 >= 224) + return 1; + + return 0; +} + +/* A trivial functon that maintains a cache of IP to MAC Address + entries. If the command is MACCACHE_GET, this func looks for the + IPv4 address in ss and fills in the 'mac' parameter and returns + true if it is found. Otherwise (not found), the function returns + false. If the command is MACCACHE_SET, the function adds an entry + with the given ip (ss) and mac address. An existing entry for the + IP ss will be overwritten with the new MAC address. true is always + returned for the set command. */ +#define MACCACHE_GET 1 +#define MACCACHE_SET 2 +static int do_mac_cache(int command, const struct sockaddr_storage *ss, u8 *mac) { + struct MacCache { + struct sockaddr_storage ip; + u8 mac[6]; + }; + static struct MacCache *Cache = NULL; + static int MacCapacity = 0; + static int MacCacheSz = 0; + int i; + + if (command == MACCACHE_GET) { + for (i = 0; i < MacCacheSz; i++) { + if (sockaddr_storage_cmp(&Cache[i].ip, ss) == 0) { + memcpy(mac, Cache[i].mac, 6); + return 1; + } + } + return 0; + } + assert(command == MACCACHE_SET); + if (MacCacheSz == MacCapacity) { + if (MacCapacity == 0) + MacCapacity = 32; + else + MacCapacity <<= 2; + Cache = (struct MacCache *) safe_realloc(Cache, MacCapacity * sizeof(struct MacCache)); + } + + /* Ensure that it isn't already there ... */ + for (i = 0; i < MacCacheSz; i++) { + if (sockaddr_storage_cmp(&Cache[i].ip, ss) == 0) { + memcpy(Cache[i].mac, mac, 6); + return 1; + } + } + + /* Add it to the end of the list */ + memcpy(&Cache[i].ip, ss, sizeof(struct sockaddr_storage)); + memcpy(Cache[i].mac, mac, 6); + MacCacheSz++; + return 1; +} + +/* A couple of trivial functions that maintain a cache of IP to MAC + * Address entries. Function mac_cache_get() looks for the IPv4 address + * in ss and fills in the 'mac' parameter and returns true if it is + * found. Otherwise (not found), the function returns false. + * Function mac_cache_set() adds an entry with the given ip (ss) and + * mac address. An existing entry for the IP ss will be overwritten + * with the new MAC address. mac_cache_set() always returns true. + * WARNING: The caller must ensure that the supplied "ss" is of family + * AF_INET. Otherwise the function will return 0 and there would be + * no way for the caller to tell tell the difference between an error + * or a cache miss.*/ +int mac_cache_get(const struct sockaddr_storage *ss, u8 *mac){ + return do_mac_cache(MACCACHE_GET, ss, mac); +} +int mac_cache_set(const struct sockaddr_storage *ss, u8 *mac){ + return do_mac_cache(MACCACHE_SET, ss, mac); +} + +/* Standard BSD internet checksum routine. Uses libdnet helper functions. */ +unsigned short in_cksum(u16 *ptr,int nbytes) { + int sum; + + sum = ip_cksum_add(ptr, nbytes, 0); + + return ip_cksum_carry(sum); + + return 0; +} + + +/* Return true iff this Next Header type is an extension header we must skip to + get to the upper-layer header. Types for which neither this function nor + ipv6_is_upperlayer return true are unknown and could be either. */ +static int ipv6_is_extension_header(u8 type) +{ + switch (type) { + case IP_PROTO_HOPOPTS: + case IP_PROTO_DSTOPTS: + case IP_PROTO_ROUTING: + case IP_PROTO_FRAGMENT: + /* + case IP_PROTO_ESP: + case IP_PROTO_AH: + */ + return 1; + default: + return 0; + } +} + +/* Return true iff this Next Header type is a known upper-layer protocol, one + that isn't followed by any more headers. Types for which neither this + function nor ipv6_is_upperlayer return true are unknown and could be + either. */ +static int ipv6_is_upperlayer(u8 type) +{ + switch (type) { + case IP_PROTO_NONE: + case IP_PROTO_TCP: + case IP_PROTO_UDP: + case IP_PROTO_ICMP: + case IP_PROTO_ICMPV6: + case IP_PROTO_SCTP: + return 1; + default: + return 0; + } +} + +/* upperlayer_only controls whether we require a known upper-layer protocol at + the end of the chain, or return the last readable header even if it is not an + upper-layer protocol (may even be another extension header). */ +static const void *ipv6_get_data_primitive(const struct ip6_hdr *ip6, + unsigned int *len, u8 *nxt, bool upperlayer_only) +{ + const unsigned char *p, *end; + + if (*len < sizeof(*ip6)) + return NULL; + + p = (unsigned char *) ip6; + end = p + *len; + + *nxt = ip6->ip6_nxt; + p += sizeof(*ip6); + while (p < end && ipv6_is_extension_header(*nxt)) { + if (p + 2 > end) + return NULL; + *nxt = *p; + p += (*(p + 1) + 1) * 8; + } + + *len = end - p; + if (upperlayer_only && !ipv6_is_upperlayer(*nxt)) + return NULL; + + return (char *) p; +} + +static const void *ip_get_data_primitive(const void *packet, unsigned int *len, + struct abstract_ip_hdr *hdr, bool upperlayer_only) { + const struct ip *ip; + + ip = (struct ip *) packet; + if (*len >= 20 && ip->ip_v == 4) { + struct sockaddr_in *sin; + + hdr->version = 4; + + sin = (struct sockaddr_in *) &hdr->src; + memset(&hdr->src, 0, sizeof(hdr->src)); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = ip->ip_src.s_addr; + + sin = (struct sockaddr_in *) &hdr->dst; + memset(&hdr->dst, 0, sizeof(hdr->dst)); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = ip->ip_dst.s_addr; + + hdr->proto = ip->ip_p; + hdr->ttl = ip->ip_ttl; + hdr->ipid = ntohs(ip->ip_id); + return ipv4_get_data(ip, len); + } else if (*len >= 40 && ip->ip_v == 6) { + const struct ip6_hdr *ip6 = (struct ip6_hdr *) ip; + struct sockaddr_in6 *sin6; + + hdr->version = 6; + + sin6 = (struct sockaddr_in6 *) &hdr->src; + memset(&hdr->src, 0, sizeof(hdr->src)); + sin6->sin6_family = AF_INET6; + memcpy(&sin6->sin6_addr, &ip6->ip6_src, IP6_ADDR_LEN); + + sin6 = (struct sockaddr_in6 *) &hdr->dst; + memset(&hdr->dst, 0, sizeof(hdr->dst)); + sin6->sin6_family = AF_INET6; + memcpy(&sin6->sin6_addr, &ip6->ip6_dst, IP6_ADDR_LEN); + + hdr->ttl = ip6->ip6_hlim; + hdr->ipid = ntohl(ip6->ip6_flow & IP6_FLOWLABEL_MASK); + return ipv6_get_data_primitive(ip6, len, &hdr->proto, upperlayer_only); + } + + return NULL; +} + +/* Find the beginning of the data payload in the IP packet beginning at packet. + Returns the beginning of the payload, updates *len to be the length of the + payload, and fills in hdr if successful. Otherwise returns NULL and *hdr is + undefined. */ +const void *ip_get_data(const void *packet, unsigned int *len, + struct abstract_ip_hdr *hdr) { + return ip_get_data_primitive(packet, len, hdr, true); +} + +/* As ip_get_data, except that it doesn't insist that the payload be a known + upper-layer protocol. This can matter in IPv6 where the last element of a nh + chain may be a protocol we don't know about. */ +const void *ip_get_data_any(const void *packet, unsigned int *len, + struct abstract_ip_hdr *hdr) { + return ip_get_data_primitive(packet, len, hdr, false); +} + +/* Get the upper-layer protocol from an IPv4 packet. */ +const void *ipv4_get_data(const struct ip *ip, unsigned int *len) +{ + unsigned int header_len; + + if (*len < 20) + return NULL; + header_len = ip->ip_hl * 4; + if (header_len < sizeof(*ip)) + return NULL; + if (header_len > *len) + return NULL; + *len -= header_len; + + return (char *) ip + header_len; +} + +/* Get the upper-layer protocol from an IPv6 packet. This skips over known + extension headers. The length of the upper-layer payload is stored in *len. + The protocol is stored in *nxt. Returns NULL in case of error. */ +const void *ipv6_get_data(const struct ip6_hdr *ip6, unsigned int *len, u8 *nxt) +{ + return ipv6_get_data_primitive(ip6, len, nxt, true); +} + +/* Get the protocol payload from an IPv6 packet. This skips over known extension + headers. It differs from ipv6_get_data in that it will return a result even + if the final header is not a known upper-layer protocol. */ +const void *ipv6_get_data_any(const struct ip6_hdr *ip6, unsigned int *len, u8 *nxt) +{ + return ipv6_get_data_primitive(ip6, len, nxt, false); +} + +const void *icmp_get_data(const struct icmp_hdr *icmp, unsigned int *len) +{ + unsigned int header_len; + + if (icmp->icmp_type == ICMP_TIMEXCEED || icmp->icmp_type == ICMP_UNREACH) + header_len = 8; + else + netutil_fatal("%s passed ICMP packet with unhandled type %d", __func__, icmp->icmp_type); + if (header_len > *len) + return NULL; + *len -= header_len; + + return (char *) icmp + header_len; +} + +const void *icmpv6_get_data(const struct icmpv6_hdr *icmpv6, unsigned int *len) +{ + unsigned int header_len; + + if (icmpv6->icmpv6_type == ICMPV6_TIMEXCEED || icmpv6->icmpv6_type == ICMPV6_UNREACH) + header_len = 8; + else + netutil_fatal("%s passed ICMPv6 packet with unhandled type %d", __func__, icmpv6->icmpv6_type); + if (header_len > *len) + return NULL; + *len -= header_len; + + return (char *) icmpv6 + header_len; +} + + +/* Calculate the Internet checksum of some given data concatentated with the + IPv4 pseudo-header. See RFC 1071 and TCP/IP Illustrated sections 3.2, 11.3, + and 17.3. */ +unsigned short ipv4_pseudoheader_cksum(const struct in_addr *src, + const struct in_addr *dst, u8 proto, u16 len, const void *hstart) { + struct pseudo { + struct in_addr src; + struct in_addr dst; + u8 zero; + u8 proto; + u16 length; + } hdr; + int sum; + + hdr.src = *src; + hdr.dst = *dst; + hdr.zero = 0; + hdr.proto = proto; + hdr.length = htons(len); + + /* Get the ones'-complement sum of the pseudo-header. */ + sum = ip_cksum_add(&hdr, sizeof(hdr), 0); + /* Add it to the sum of the packet. */ + sum = ip_cksum_add(hstart, len, sum); + + /* Fold in the carry, take the complement, and return. */ + sum = ip_cksum_carry(sum); + /* RFC 768: "If the computed checksum is zero, it is transmitted as all + * ones (the equivalent in one's complement arithmetic). An all zero + * transmitted checksum value means that the transmitter generated no + * checksum" */ + if (proto == IP_PROTO_UDP && sum == 0) + sum = 0xFFFF; + + return sum; +} + +/* Calculate the Internet checksum of some given data concatenated with the + IPv6 pseudo-header. See RFC 2460 section 8.1. */ +u16 ipv6_pseudoheader_cksum(const struct in6_addr *src, + const struct in6_addr *dst, u8 nxt, u32 len, const void *hstart) { + struct { + struct in6_addr src; + struct in6_addr dst; + u32 length; + u8 z0, z1, z2; + u8 nxt; + } hdr; + int sum; + + hdr.src = *src; + hdr.dst = *dst; + hdr.z0 = hdr.z1 = hdr.z2 = 0; + hdr.length = htonl(len); + hdr.nxt = nxt; + + sum = ip_cksum_add(&hdr, sizeof(hdr), 0); + sum = ip_cksum_add(hstart, len, sum); + sum = ip_cksum_carry(sum); + /* RFC 2460: "Unlike IPv4, when UDP packets are originated by an IPv6 node, + the UDP checksum is not optional. That is, whenever originating a UDP + packet, an IPv6 node must compute a UDP checksum over the packet and the + pseudo-header, and, if that computation yields a result of zero, it must be + changed to hex FFFF for placement in the UDP header." */ + if (nxt == IP_PROTO_UDP && sum == 0) + sum = 0xFFFF; + + return sum; +} + +void sethdrinclude(int sd) { +#ifdef IP_HDRINCL + int one = 1; + setsockopt(sd, IPPROTO_IP, IP_HDRINCL, (const char *) &one, sizeof(one)); +#endif +} + +void set_ipoptions(int sd, void *opts, size_t optslen) { +#ifdef IP_OPTIONS + if (sd == -1) + return; + + setsockopt(sd, IPPROTO_IP, IP_OPTIONS, (const char *) opts, optslen); +#endif +} + +void set_ttl(int sd, int ttl) { +#ifdef IP_TTL + if (sd == -1) + return; + + setsockopt(sd, IPPROTO_IP, IP_TTL, (const char *) &ttl, sizeof ttl); +#endif +} + +/* Other than WIN32, what these systems have in common is that they use BPF for + packet capture. (Solaris 10 and earlier used DLPI and had valid selectable + fds.) */ +#if defined(WIN32) || defined(MACOSX) || (defined(FREEBSD) && (__FreeBSD_version < 500000)) || defined(SOLARIS_BPF_PCAP_CAPTURE) || defined(OPENBSD) +/* Returns whether the system supports pcap_get_selectable_fd() properly */ +int pcap_selectable_fd_valid() { + return 0; +} + +/* Call this instead of pcap_get_selectable_fd directly (or your code + won't compile on Windows). On systems which don't seem to support + the pcap_get_selectable_fd() function properly, returns -1, + otherwise simply calls pcap_selectable_fd and returns the + results. If you just want to test whether the function is supported, + use pcap_selectable_fd_valid() instead. */ +int my_pcap_get_selectable_fd(pcap_t *p) { + return -1; +} +#else +int pcap_selectable_fd_valid() { + return 1; +} +int my_pcap_get_selectable_fd(pcap_t *p) { + return pcap_get_selectable_fd(p); +} +#endif + +/* Are we guaranteed to be able to read exactly one frame for each time the pcap + fd is selectable? If not, it's possible for the fd to become selectable, then + for pcap_dispatch to buffer two or more frames, and return only the first one + Because select doesn't know about pcap's buffer, the fd does not become + selectable again, even though another pcap_next_ex would succeed. On these + platforms, we must do a non-blocking read from the fd before doing a select + on the fd. + + It is guaranteed that if pcap_selectable_fd_valid() is false, then so is the + return value of this function. */ +int pcap_selectable_fd_one_to_one() { +#ifdef SOLARIS + return 0; +#endif + return pcap_selectable_fd_valid(); +} + + +/* returns -1 if we can't use select() on the pcap device, 0 for timeout, and + * >0 for success. If select() fails we bail out because it couldn't work with + * the file descriptor we got from my_pcap_get_selectable_fd() + */ +int pcap_select(pcap_t *p, struct timeval *timeout) { + int ret; +#ifdef WIN32 + DWORD msec_timeout = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; + HANDLE event = pcap_getevent(p); + DWORD result = WaitForSingleObject(event, msec_timeout); + + switch(result) { + case WAIT_OBJECT_0: + ret = 1; + break; + case WAIT_TIMEOUT: + ret = 0; + break; + case WAIT_FAILED: + ret = -1; + netutil_error("%s: WaitForSingleObject failed: %d", __func__, GetLastError()); + break; + default: + ret = -1; + netutil_fatal("%s: WaitForSingleObject returned unknown result: %x", __func__, result); + break; + } + +#else + int fd; + fd_set rfds; + + if ((fd = my_pcap_get_selectable_fd(p)) == -1) + return -1; + + FD_ZERO(&rfds); + checked_fd_set(fd, &rfds); + + do { + errno = 0; + ret = select(fd + 1, &rfds, NULL, NULL, timeout); + if (ret == -1) { + if (errno == EINTR) + netutil_error("%s: %s", __func__, strerror(errno)); + else + netutil_fatal("Your system does not support select()ing on pcap devices (%s). PLEASE REPORT THIS ALONG WITH DETAILED SYSTEM INFORMATION TO THE nmap-dev MAILING LIST!", strerror(errno)); + } + } while (ret == -1); + +#endif + return ret; +} + +int pcap_select(pcap_t *p, long usecs) { + struct timeval tv; + + tv.tv_sec = usecs / 1000000; + tv.tv_usec = usecs % 1000000; + + return pcap_select(p, &tv); +} + + +/* These two are for eth_open_cached() and eth_close_cached() */ +static char etht_cache_device_name[64]; +static eth_t *etht_cache_device = NULL; + +/* A simple function that caches the eth_t from dnet for one device, + to avoid opening, closing, and re-opening it thousands of tims. If + you give a different device, this function will close the first + one. Thus this should never be used by programs that need to deal + with multiple devices at once. In addition, you MUST NEVER + eth_close() A DEVICE OBTAINED FROM THIS FUNCTION. Instead, you can + call eth_close_cached() to close whichever device (if any) is + cached. Returns NULL if it fails to open the device. */ +eth_t *eth_open_cached(const char *device) { + if (!device) + netutil_fatal("%s() called with NULL device name!", __func__); + if (!*device) + netutil_fatal("%s() called with empty device name!", __func__); + + if (strcmp(device, etht_cache_device_name) == 0) { + /* Yay, we have it cached. */ + return etht_cache_device; + } + + if (*etht_cache_device_name) { + eth_close(etht_cache_device); + etht_cache_device_name[0] = '\0'; + etht_cache_device = NULL; + } + + etht_cache_device = eth_open(device); + if (etht_cache_device) + Strncpy(etht_cache_device_name, device, + sizeof(etht_cache_device_name)); + + return etht_cache_device; +} + +/* See the description for eth_open_cached */ +void eth_close_cached() { + if (etht_cache_device) { + eth_close(etht_cache_device); + etht_cache_device = NULL; + etht_cache_device_name[0] = '\0'; + } + return; +} + +/* Takes a protocol number like IPPROTO_TCP, IPPROTO_UDP, IPPROTO_IP, + * etc, and returns an ASCII representation (or the string "unknown" if + * it doesn't recognize the number). If uppercase is non zero, the + * returned value will be in uppercase letters, otherwise it'll be + * in lowercase */ +const char *proto2ascii_case(u8 proto, int uppercase) { + switch (proto) { + + case IPPROTO_TCP: + return uppercase ? "TCP" : "tcp"; + break; + case IPPROTO_UDP: + return uppercase ? "UDP" : "udp"; + break; + case IPPROTO_SCTP: + return uppercase ? "SCTP" : "sctp"; + break; + case IPPROTO_IP: + return uppercase ? "IP" : "ip"; + break; +#ifdef IPPROTO_ICMP + case IPPROTO_ICMP: + return uppercase ? "ICMP" : "icmp"; + break; +#endif +#ifdef IPPROTO_IPV6 + case IPPROTO_IPV6: + return uppercase ? "IPv6" : "ipv6"; + break; +#endif +#ifdef IPPROTO_ICMPV6 + case IPPROTO_ICMPV6: + return uppercase ? "ICMPv6" : "icmpv6"; + break; +#endif +#ifdef IPPROTO_GRE + case IPPROTO_GRE: // Generic Routing Encapsulation + return uppercase ? "GRE" : "gre"; + break; +#endif +#ifdef IPPROTO_ESP + case IPPROTO_ESP: // Encapsulating Security Payload (IPSec) + return uppercase ? "IPSec/ESP" : "ipsec/esp"; + break; +#endif +#ifdef IPPROTO_AH + case IPPROTO_AH: // Authentication Header (IPSec) + return uppercase ? "IPSec/AH" : "ipsec/ah"; + break; +#endif + default: + return uppercase ? "UNKNOWN" : "unknown"; + } + + return NULL; // Unreached +} + +const char *proto2ascii_lowercase(u8 proto) { + return proto2ascii_case(proto, 0); +} +const char *proto2ascii_uppercase(u8 proto) { + return proto2ascii_case(proto, 1); +} + +/* 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 tcppacketoptinfo(u8 *optp, int len, char *result, int bufsize) { + assert(optp); + assert(result); + char *p, ch; + 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 %hu", (unsigned short) 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 %lu %lu", (unsigned long) ntohl(tmpword1), + (unsigned long) ntohl(tmpword2)); + bufsize -= strlen(p); + p += strlen(p); + + q += 8; + len -= 10; + } + + ch = ','; + } + + if (len > 0) { + *result = '\0'; + return; + } + + Snprintf(p, bufsize, ">"); +} + + + +/* A trivial function used with qsort to sort the routes by netmask and metric */ +static int routecmp(const void *a, const void *b) { + struct sys_route *r1 = (struct sys_route *) a; + struct sys_route *r2 = (struct sys_route *) b; + if (r1->dest.ss_family < r2->dest.ss_family) + return -1; + else if (r1->dest.ss_family > r2->dest.ss_family) + return 1; + + if (r1->netmask_bits < r2->netmask_bits) + return 1; + else if (r1->netmask_bits > r2->netmask_bits) + return -1; + + if (r1->metric < r2->metric) + return -1; + else if (r1->metric > r2->metric) + return 1; + + /* Compare addresses of equal elements to make the sort stable, as suggested + by the Glibc manual. */ + if (a < b) + return -1; + else if (a > b) + return 1; + else + return 0; +} + + + +/* Convert an address to a string and back again. The first parsing step + eliminates magical OS-specific syntax, for example on OS X, fe80:4::X:X:X:X + becomes "fe80::X:X:X:X" (the "4" in this case is another way of writing the + zone ID, like "%en0"; i.e., in this case en0 is interface number 4). This + must be done before e.g. comparing addresses by netmask. */ +static int canonicalize_address(const struct sockaddr_storage *ss, + struct sockaddr_storage *output) { + char canonical_ip_string[NI_MAXHOST]; + struct addrinfo hints; + struct addrinfo *ai; + int rc; + + /* Convert address to string. */ + rc = getnameinfo((struct sockaddr *) ss, sizeof(*ss), + canonical_ip_string, sizeof(canonical_ip_string), NULL, 0, NI_NUMERICHOST); + if (rc != 0) { + /* Don't care. */ + *output = *ss; + return 0; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ss->ss_family; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags |= AI_NUMERICHOST; + + rc = getaddrinfo(canonical_ip_string, NULL, &hints, &ai); + if (rc != 0 || ai == NULL) + return -1; + assert(ai->ai_addrlen > 0 && ai->ai_addrlen <= (int) sizeof(*output)); + memcpy(output, ai->ai_addr, ai->ai_addrlen); + freeaddrinfo(ai); + + return 0; +} + +static int collect_dnet_interfaces(const struct intf_entry *entry, void *arg) { + struct dnet_collector_route_nfo *dcrn = (struct dnet_collector_route_nfo *) arg; + bool primary_done; + unsigned int num_aliases_done; + struct sockaddr_storage tmpss; + int rc; + + primary_done = false; + num_aliases_done = 0; + while (!primary_done || num_aliases_done < entry->intf_alias_num) { + /* Make sure we have room for the new route */ + if (dcrn->numifaces >= dcrn->capacity) { + dcrn->capacity <<= 2; + dcrn->ifaces = (struct interface_info *) safe_realloc(dcrn->ifaces, + dcrn->capacity * sizeof(struct interface_info)); + } + + /* The first time through the loop we add the primary interface record. + After that we add the aliases one at a time. */ + if (!primary_done) { + if ( (addr_ntos(&entry->intf_addr, (struct sockaddr *) &tmpss) == -1) +#ifdef AF_LINK + || (tmpss.ss_family == AF_LINK) +#endif + ) { + dcrn->ifaces[dcrn->numifaces].addr.ss_family = 0; + } else { + rc = canonicalize_address(&tmpss, &dcrn->ifaces[dcrn->numifaces].addr); + assert(rc == 0); + } + dcrn->ifaces[dcrn->numifaces].netmask_bits = entry->intf_addr.addr_bits; + primary_done = true; + } else if (num_aliases_done < entry->intf_alias_num) { + if ( (addr_ntos(&entry->intf_alias_addrs[num_aliases_done], (struct sockaddr *) &tmpss) == -1) +#ifdef AF_LINK + || (tmpss.ss_family == AF_LINK) +#endif + ) { + dcrn->ifaces[dcrn->numifaces].addr.ss_family = 0; + } else { + rc = canonicalize_address(&tmpss, &dcrn->ifaces[dcrn->numifaces].addr); + assert(rc == 0); + } + dcrn->ifaces[dcrn->numifaces].netmask_bits = entry->intf_alias_addrs[num_aliases_done].addr_bits; + num_aliases_done++; + } + + /* OK, address/netmask found. Let's get the name */ + Strncpy(dcrn->ifaces[dcrn->numifaces].devname, entry->intf_name, + sizeof(dcrn->ifaces[dcrn->numifaces].devname)); + Strncpy(dcrn->ifaces[dcrn->numifaces].devfullname, entry->intf_name, + sizeof(dcrn->ifaces[dcrn->numifaces].devfullname)); + + /* Interface type */ + if (entry->intf_type == INTF_TYPE_ETH && (entry->intf_flags & INTF_FLAG_NOARP) == 0) { + dcrn->ifaces[dcrn->numifaces].device_type = devt_ethernet; + /* Collect the MAC address since this is ethernet */ + memcpy(dcrn->ifaces[dcrn->numifaces].mac, &entry->intf_link_addr.addr_eth.data, 6); + } else if (entry->intf_type == INTF_TYPE_LOOPBACK) { + dcrn->ifaces[dcrn->numifaces].device_type = devt_loopback; + } else if (entry->intf_type == INTF_TYPE_TUN) { + dcrn->ifaces[dcrn->numifaces].device_type = devt_p2p; + } else { + dcrn->ifaces[dcrn->numifaces].device_type = devt_other; + } + + dcrn->ifaces[dcrn->numifaces].ifindex = entry->intf_index; + + dcrn->ifaces[dcrn->numifaces].mtu = entry->intf_mtu; + + /* Is the interface up and running? */ + dcrn->ifaces[dcrn->numifaces].device_up = (entry->intf_flags & INTF_FLAG_UP) ? true : false; + + /* For the rest of the information, we must open the interface directly ... */ + dcrn->numifaces++; + } + + return 0; +} + +/* Get a list of interfaces using dnet and intf_loop. */ +static struct interface_info *getinterfaces_dnet(int *howmany, char *errstr, size_t errstrlen) { + struct dnet_collector_route_nfo dcrn; + intf_t *it; + + dcrn.routes = NULL; + dcrn.numroutes = 0; + dcrn.numifaces = 0; + + assert(howmany); + + /* Initialize the interface array. */ + dcrn.capacity = 16; + dcrn.ifaces = (struct interface_info *) safe_zalloc(sizeof(struct interface_info) * dcrn.capacity); + + it = intf_open(); + if (!it){ + if(errstr) Snprintf(errstr, errstrlen, "%s: intf_open() failed", __func__); + *howmany=-1; + return NULL; + } + if (intf_loop(it, collect_dnet_interfaces, &dcrn) != 0){ + if(errstr) Snprintf(errstr, errstrlen, "%s: intf_loop() failed", __func__); + *howmany=-1; + return NULL; + } + intf_close(it); + + *howmany = dcrn.numifaces; + return dcrn.ifaces; +} + +static struct interface_info *mydevs = NULL; +/* Returns an allocated array of struct interface_info representing the + available interfaces. The number of interfaces is returned in *howmany. This + function just does caching of results; the real work is done in + getinterfaces_dnet(). + On error, NULL is returned, howmany is set to -1 and the supplied + error buffer "errstr", if not NULL, will contain an error message. */ +struct interface_info *getinterfaces(int *howmany, char *errstr, size_t errstrlen) { + static int numifaces = 0; + + if (mydevs == NULL) { + mydevs = getinterfaces_dnet(&numifaces, errstr, errstrlen); + } + + /* These will propagate any error produced in getinterfaces_xxxx() to + * the caller. */ + if (howmany) + *howmany = numifaces; + return mydevs; +} + +void freeinterfaces(void) { + free(mydevs); + mydevs = NULL; +} + +/* The 'dev' passed in must be at least 32 bytes long. Returns 0 on success. */ +int ipaddr2devname(char *dev, const struct sockaddr_storage *addr) { + struct interface_info *ifaces; + int numifaces; + int i; + + ifaces = getinterfaces(&numifaces, NULL, 0); + + if (ifaces == NULL) + return -1; + + for (i = 0; i < numifaces; i++) { + if (sockaddr_storage_cmp(&ifaces[i].addr, addr) == 0) { + Strncpy(dev, ifaces[i].devname, 32); + return 0; + } + } + + return -1; +} + +int devname2ipaddr(char *dev, struct sockaddr_storage *addr) { + struct interface_info *ifaces; + int numifaces; + int i; + ifaces = getinterfaces(&numifaces, NULL, 0); + + if (ifaces == NULL) + return -1; + + for (i = 0; i < numifaces; i++) { + if (!strcmp(dev, ifaces[i].devfullname)) { + *addr = ifaces[i].addr; + return 0; + } + } + return -1; +} + +/* Looks for an interface with the given name (iname) and address + family type, and returns the corresponding interface_info if found. + Will accept a match of devname or devfullname. Returns NULL if + none found */ +struct interface_info *getInterfaceByName(const char *iname, int af) { + struct interface_info *ifaces; + int numifaces = 0; + int ifnum; + + ifaces = getinterfaces(&numifaces, NULL, 0); + + for (ifnum = 0; ifnum < numifaces; ifnum++) { + if ((strcmp(ifaces[ifnum].devfullname, iname) == 0 || + strcmp(ifaces[ifnum].devname, iname) == 0) && + ifaces[ifnum].addr.ss_family == af) + return &ifaces[ifnum]; + } + + return NULL; +} + + +int sockaddr_equal(const struct sockaddr_storage *a, + const struct sockaddr_storage *b) { + + if (a->ss_family == AF_INET && b->ss_family == AF_INET) { + struct sockaddr_in *sa, *sb; + + sa = (struct sockaddr_in *) a; + sb = (struct sockaddr_in *) b; + + return sa->sin_addr.s_addr == sb->sin_addr.s_addr; + } if (a->ss_family == AF_INET6 && b->ss_family == AF_INET6) { + struct sockaddr_in6 *sa, *sb; + + sa = (struct sockaddr_in6 *) a; + sb = (struct sockaddr_in6 *) b; + + return memcmp(sa->sin6_addr.s6_addr, sb->sin6_addr.s6_addr, sizeof(sa->sin6_addr.s6_addr)) == 0; + } + + return 0; +} + +int sockaddr_equal_netmask(const struct sockaddr_storage *a, + const struct sockaddr_storage *b, u16 nbits) { + unsigned char netmask[IP6_ADDR_LEN]; + + addr_btom(nbits, netmask, sizeof(netmask)); + + if (a->ss_family == AF_INET && b->ss_family == AF_INET) { + struct in_addr *sa, *sb, *sn; + + sa = &((struct sockaddr_in *) a)->sin_addr; + sb = &((struct sockaddr_in *) b)->sin_addr; + sn = (struct in_addr *) netmask; + + return (sa->s_addr & sn->s_addr) == (sb->s_addr & sn->s_addr); + } else if (a->ss_family == AF_INET6 && b->ss_family == AF_INET6) { + struct in6_addr *sa, *sb, *sn; + unsigned int i; + + sa = &((struct sockaddr_in6 *) a)->sin6_addr; + sb = &((struct sockaddr_in6 *) b)->sin6_addr; + sn = (struct in6_addr *) netmask; + + for (i = 0; i < sizeof(sa->s6_addr); i++) { + if ((sa->s6_addr[i] & sn->s6_addr[i]) != (sb->s6_addr[i] & sn->s6_addr[i])) { + return 0; + } + } + + return 1; + } + + return 0; +} + +int sockaddr_equal_zero(const struct sockaddr_storage *s) { + if (s->ss_family == AF_INET) { + const struct sockaddr_in *sin; + + sin = (struct sockaddr_in *) s; + return sin->sin_addr.s_addr == 0; + } if (s->ss_family == AF_INET6) { + const struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *) s; + return memcmp(sin6->sin6_addr.s6_addr, IP6_ADDR_UNSPEC, IP6_ADDR_LEN) == 0; + } + + return 0; +} + +/* This is a helper for getsysroutes_dnet. Once the table of routes is in + place, this function assigns each to an interface and removes any routes + that can't be assigned. */ +static struct dnet_collector_route_nfo *sysroutes_dnet_find_interfaces(struct dnet_collector_route_nfo *dcrn) +{ + struct interface_info *ifaces; + int numifaces = 0; + int i, j; + int changed=0; + + if( (ifaces=getinterfaces(&numifaces, NULL, 0))==NULL ) + return NULL; + for (i = 0; i < dcrn->numroutes; i++) { + if (dcrn->routes[i].device != NULL) + continue; + + /* First we match up routes whose gateway or destination address + directly matches the address of an interface. */ + struct sys_route *route = &dcrn->routes[i]; + struct sockaddr_storage *routeaddr; + + /* First see if the gateway was set */ + if (sockaddr_equal_zero(&route->gw)) + routeaddr = &dcrn->routes[i].dest; + else + routeaddr = &dcrn->routes[i].gw; + + for (j = 0; j < numifaces; j++) { + if (sockaddr_equal_netmask(&ifaces[j].addr, routeaddr, ifaces[j].netmask_bits)) { + dcrn->routes[i].device = &ifaces[j]; + break; + } + } + } + + /* Find any remaining routes that don't yet have an interface, and try to + match them up with the interface of another route. This handles "two-step" + routes like sometimes exist with PPP, where the gateway address of the + default route doesn't match an interface address, but the gateway address + goes through another route that does have an interface. */ + + do { + changed = 0; + for (i = 0; i < dcrn->numroutes; i++) { + if (dcrn->routes[i].device != NULL) + continue; + /* Does this route's gateway go through another route with an assigned + interface? */ + for (j = 0; j < dcrn->numroutes; j++) { + if (sockaddr_equal(&dcrn->routes[i].gw, &dcrn->routes[j].dest) + && dcrn->routes[j].device != NULL) { + dcrn->routes[i].device = dcrn->routes[j].device; + changed = 1; + } + } + } + } while (changed); + + /* Cull any routes that still don't have an interface. */ + i = 0; + while (i < dcrn->numroutes) { + if (dcrn->routes[i].device == NULL) { + char destbuf[INET6_ADDRSTRLEN]; + char gwbuf[INET6_ADDRSTRLEN]; + + Strncpy(destbuf, inet_ntop_ez(&dcrn->routes[i].dest, sizeof(dcrn->routes[i].dest)), sizeof(destbuf)); + Strncpy(gwbuf, inet_ntop_ez(&dcrn->routes[i].gw, sizeof(dcrn->routes[i].gw)), sizeof(gwbuf)); + /* + netutil_error("WARNING: Unable to find appropriate interface for system route to %s/%u gw %s", + destbuf, dcrn->routes[i].netmask_bits, gwbuf); + */ + /* Remove this entry from the table. */ + memmove(dcrn->routes + i, dcrn->routes + i + 1, sizeof(dcrn->routes[0]) * (dcrn->numroutes - i - 1)); + dcrn->numroutes--; + } else { + i++; + } + } + + return dcrn; +} + + +/* This is the callback for the call to route_loop in getsysroutes_dnet. It + takes a route entry and adds it into the dnet_collector_route_nfo struct. */ +static int collect_dnet_routes(const struct route_entry *entry, void *arg) { + struct dnet_collector_route_nfo *dcrn = (struct dnet_collector_route_nfo *) arg; + + /* Make sure we have room for the new route */ + if (dcrn->numroutes >= dcrn->capacity) { + dcrn->capacity <<= 2; + dcrn->routes = (struct sys_route *) safe_realloc(dcrn->routes, dcrn->capacity * sizeof(struct sys_route)); + } + + /* Now for the important business */ + addr_ntos(&entry->route_dst, (struct sockaddr *) &dcrn->routes[dcrn->numroutes].dest); + dcrn->routes[dcrn->numroutes].netmask_bits = entry->route_dst.addr_bits; + addr_ntos(&entry->route_gw, (struct sockaddr *) &dcrn->routes[dcrn->numroutes].gw); + dcrn->routes[dcrn->numroutes].metric = entry->metric; + dcrn->routes[dcrn->numroutes].device = getInterfaceByName(entry->intf_name, dcrn->routes[dcrn->numroutes].dest.ss_family); + dcrn->numroutes++; + + return 0; +} + + +/* Read system routes via libdnet. */ +static struct sys_route *getsysroutes_dnet(int *howmany, char *errstr, size_t errstrlen) { + struct dnet_collector_route_nfo dcrn; + + dcrn.capacity = 128; + dcrn.routes = (struct sys_route *) safe_zalloc(dcrn.capacity * sizeof(struct sys_route)); + dcrn.numroutes = 0; + dcrn.ifaces = NULL; + dcrn.numifaces = 0; + assert(howmany); + route_t *dr = route_open(); + + if (!dr){ + if(errstr) Snprintf(errstr, errstrlen, "%s: route_open() failed", __func__); + *howmany=-1; + return NULL; + } + if (route_loop(dr, collect_dnet_routes, &dcrn) != 0) { + if(errstr) Snprintf(errstr, errstrlen, "%s: route_loop() failed", __func__); + *howmany=-1; + return NULL; + } + route_close(dr); + + /* Now match up the routes to interfaces. */ + if( sysroutes_dnet_find_interfaces(&dcrn) == NULL ){ + if(errstr) Snprintf(errstr, errstrlen, "%s: sysroutes_dnet_find_interfaces() failed", __func__); + return NULL; + } + + *howmany = dcrn.numroutes; + return dcrn.routes; +} + + +/* Parse the system routing table, converting each route into a + sys_route entry. Returns an array of sys_routes. numroutes is set + to the number of routes in the array. The routing table is only + read the first time this is called -- later results are cached. + The returned route array is sorted by netmask with the most + specific matches first. + On error, NULL is returned, howmany is set to -1 and the supplied + error buffer "errstr", if not NULL, will contain an error message. */ +struct sys_route *getsysroutes(int *howmany, char *errstr, size_t errstrlen) { + static struct sys_route *routes = NULL; + static int numroutes = 0; + assert(howmany); + + if (routes != NULL) { + /* We have it cached. */ + *howmany = numroutes; + return routes; + } + + routes = getsysroutes_dnet(howmany, errstr, errstrlen); + + /* Check if we managed to get the routes and sort them if we did */ + if(routes==NULL){ + *howmany=-1; + return NULL; + }else{ + numroutes = *howmany; + /* Ensure that the route array is sorted by netmask and metric */ + qsort(routes, numroutes, sizeof(routes[0]), routecmp); + } + return routes; +} + + +/* Tries to determine whether the supplied address corresponds to + * localhost. (eg: the address is something like 127.x.x.x, the address + * matches one of the local network interfaces' address, etc). + * Returns 1 if the address is thought to be localhost and 0 otherwise */ +int islocalhost(const struct sockaddr_storage *ss) { + char dev[128]; + struct sockaddr_in *sin = NULL; + struct sockaddr_in6 *sin6 = NULL; + + if (ss->ss_family == AF_INET){ + sin = (struct sockaddr_in *) ss; + /* If it is 0.0.0.0 or starts with 127 then it is probably localhost. */ + if ((sin->sin_addr.s_addr & htonl(0xFF000000)) == htonl(0x7F000000)) + return 1; + + if (!(sin->sin_addr.s_addr)) + return 1; + } else { + sin6 = (struct sockaddr_in6 *) ss; + /* If it is ::0 or ::1 then it is probably localhost. */ + if (memcmp(&(sin6->sin6_addr), IP6_ADDR_UNSPEC, IP6_ADDR_LEN) == 0) + return 1; + if (memcmp(&(sin6->sin6_addr), IP6_ADDR_LOOPBACK, IP6_ADDR_LEN) == 0) + return 1; + } + + /* If it is the same addy as a local interface, then it is + probably localhost */ + if (ipaddr2devname(dev, ss) != -1) + return 1; + + /* OK, so to a first approximation, this addy is probably not + localhost */ + return 0; +} + + +/* Determines whether the supplied address corresponds to a private, + * non-Internet-routable address. See RFC1918 for details. + * + * Also checks for link-local addressing per RFC3927. + * + * Returns 1 if the address is private or 0 otherwise. */ +int isipprivate(const struct sockaddr_storage *addr) { + const struct sockaddr_in *sin; + char *ipc; + unsigned char i1, i2; + + if (!addr) + return 0; + if (addr->ss_family != AF_INET) + return 0; + sin = (struct sockaddr_in *) addr; + + ipc = (char *) &(sin->sin_addr.s_addr); + i1 = ipc[0]; + i2 = ipc[1]; + + /* 10.0.0.0/8 */ + if (i1 == 10) + return 1; + + /* 172.16.0.0/12 */ + if (i1 == 172 && i2 >= 16 && i2 <= 31) + return 1; + + /* 169.254.0.0/16 - RFC 3927 */ + if (i1 == 169 && i2 == 254) + return 1; + + /* 192.168.0.0/16 */ + if (i1 == 192 && i2 == 168) + return 1; + + return 0; +} + + +char *nexthdrtoa(u8 nextheader, int acronym){ + +static char buffer[129]; +memset(buffer, 0, 129); + +#define HDRTOA(num, short_name, long_name) \ + case num: \ + strncpy(buffer, acronym ? short_name : long_name, 128);\ + break; + +switch(nextheader){ + /* Generate these lines from nmap-protocols using the following perl command: + perl -lne'if(/^(\S+)\s*(\d+)\s*\#?\s*(.*)/){my$l=$3||$1;print qq{HDRTOA($2, "$1", "$l")}}' + */ + HDRTOA(0, "hopopt", "IPv6 Hop-by-Hop Option") + HDRTOA(1, "icmp", "Internet Control Message") + HDRTOA(2, "igmp", "Internet Group Management") + HDRTOA(3, "ggp", "Gateway-to-Gateway") + HDRTOA(4, "ipv4", "IP in IP (encapsulation)") + HDRTOA(5, "st", "Stream") + HDRTOA(6, "tcp", "Transmission Control") + HDRTOA(7, "cbt", "CBT") + HDRTOA(8, "egp", "Exterior Gateway Protocol") + HDRTOA(9, "igp", "any private interior gateway") + HDRTOA(10, "bbn-rcc-mon", "BBN RCC Monitoring") + HDRTOA(11, "nvp-ii", "Network Voice Protocol") + HDRTOA(12, "pup", "PARC universal packet protocol") + HDRTOA(13, "argus", "ARGUS") + HDRTOA(14, "emcon", "EMCON") + HDRTOA(15, "xnet", "Cross Net Debugger") + HDRTOA(16, "chaos", "Chaos") + HDRTOA(17, "udp", "User Datagram") + HDRTOA(18, "mux", "Multiplexing") + HDRTOA(19, "dcn-meas", "DCN Measurement Subsystems") + HDRTOA(20, "hmp", "Host Monitoring") + HDRTOA(21, "prm", "Packet Radio Measurement") + HDRTOA(22, "xns-idp", "XEROX NS IDP") + HDRTOA(23, "trunk-1", "Trunk-1") + HDRTOA(24, "trunk-2", "Trunk-2") + HDRTOA(25, "leaf-1", "Leaf-1") + HDRTOA(26, "leaf-2", "Leaf-2") + HDRTOA(27, "rdp", "Reliable Data Protocol") + HDRTOA(28, "irtp", "Internet Reliable Transaction") + HDRTOA(29, "iso-tp4", "ISO Transport Protocol Class 4") + HDRTOA(30, "netblt", "Bulk Data Transfer Protocol") + HDRTOA(31, "mfe-nsp", "MFE Network Services Protocol") + HDRTOA(32, "merit-inp", "MERIT Internodal Protocol") + HDRTOA(33, "dccp", "Datagram Congestion Control Protocol") + HDRTOA(34, "3pc", "Third Party Connect Protocol") + HDRTOA(35, "idpr", "Inter-Domain Policy Routing Protocol") + HDRTOA(36, "xtp", "XTP") + HDRTOA(37, "ddp", "Datagram Delivery Protocol") + HDRTOA(38, "idpr-cmtp", "IDPR Control Message Transport Proto") + HDRTOA(39, "tp++", "TP+") + HDRTOA(40, "il", "IL Transport Protocol") + HDRTOA(41, "ipv6", "Ipv6") + HDRTOA(42, "sdrp", "Source Demand Routing Protocol") + HDRTOA(43, "ipv6-route", "Routing Header for IPv6") + HDRTOA(44, "ipv6-frag", "Fragment Header for IPv6") + HDRTOA(45, "idrp", "Inter-Domain Routing Protocol") + HDRTOA(46, "rsvp", "Reservation Protocol") + HDRTOA(47, "gre", "General Routing Encapsulation") + HDRTOA(48, "dsp", "Dynamic Source Routing Protocol. Historically MHRP") + HDRTOA(49, "bna", "BNA") + HDRTOA(50, "esp", "Encap Security Payload") + HDRTOA(51, "ah", "Authentication Header") + HDRTOA(52, "i-nlsp", "Integrated Net Layer Security TUBA") + HDRTOA(53, "swipe", "IP with Encryption") + HDRTOA(54, "narp", "NBMA Address Resolution Protocol") + HDRTOA(55, "mobile", "IP Mobility") + HDRTOA(56, "tlsp", "Transport Layer Security Protocol using Kryptonet key management") + HDRTOA(57, "skip", "SKIP") + HDRTOA(58, "ipv6-icmp", "ICMP for IPv6") + HDRTOA(59, "ipv6-nonxt", "No Next Header for IPv6") + HDRTOA(60, "ipv6-opts", "Destination Options for IPv6") + HDRTOA(61, "anyhost", "any host internal protocol") + HDRTOA(62, "cftp", "CFTP") + HDRTOA(63, "anylocalnet", "any local network") + HDRTOA(64, "sat-expak", "SATNET and Backroom EXPAK") + HDRTOA(65, "kryptolan", "Kryptolan") + HDRTOA(66, "rvd", "MIT Remote Virtual Disk Protocol") + HDRTOA(67, "ippc", "Internet Pluribus Packet Core") + HDRTOA(68, "anydistribfs", "any distributed file system") + HDRTOA(69, "sat-mon", "SATNET Monitoring") + HDRTOA(70, "visa", "VISA Protocol") + HDRTOA(71, "ipcv", "Internet Packet Core Utility") + HDRTOA(72, "cpnx", "Computer Protocol Network Executive") + HDRTOA(73, "cphb", "Computer Protocol Heart Beat") + HDRTOA(74, "wsn", "Wang Span Network") + HDRTOA(75, "pvp", "Packet Video Protocol") + HDRTOA(76, "br-sat-mon", "Backroom SATNET Monitoring") + HDRTOA(77, "sun-nd", "SUN ND PROTOCOL-Temporary") + HDRTOA(78, "wb-mon", "WIDEBAND Monitoring") + HDRTOA(79, "wb-expak", "WIDEBAND EXPAK") + HDRTOA(80, "iso-ip", "ISO Internet Protocol") + HDRTOA(81, "vmtp", "VMTP") + HDRTOA(82, "secure-vmtp", "SECURE-VMTP") + HDRTOA(83, "vines", "VINES") + HDRTOA(84, "iptm", "Internet Protocol Traffic Manager. Historically TTP") + HDRTOA(85, "nsfnet-igp", "NSFNET-IGP") + HDRTOA(86, "dgp", "Dissimilar Gateway Protocol") + HDRTOA(87, "tcf", "TCF") + HDRTOA(88, "eigrp", "EIGRP") + HDRTOA(89, "ospfigp", "OSPFIGP") + HDRTOA(90, "sprite-rpc", "Sprite RPC Protocol") + HDRTOA(91, "larp", "Locus Address Resolution Protocol") + HDRTOA(92, "mtp", "Multicast Transport Protocol") + HDRTOA(93, "ax.25", "AX.") + HDRTOA(94, "ipip", "IP-within-IP Encapsulation Protocol") + HDRTOA(95, "micp", "Mobile Internetworking Control Pro.") + HDRTOA(96, "scc-sp", "Semaphore Communications Sec.") + HDRTOA(97, "etherip", "Ethernet-within-IP Encapsulation") + HDRTOA(98, "encap", "Encapsulation Header") + HDRTOA(99, "anyencrypt", "any private encryption scheme") + HDRTOA(100, "gmtp", "GMTP") + HDRTOA(101, "ifmp", "Ipsilon Flow Management Protocol") + HDRTOA(102, "pnni", "PNNI over IP") + HDRTOA(103, "pim", "Protocol Independent Multicast") + HDRTOA(104, "aris", "ARIS") + HDRTOA(105, "scps", "SCPS") + HDRTOA(106, "qnx", "QNX") + HDRTOA(107, "a/n", "Active Networks") + HDRTOA(108, "ipcomp", "IP Payload Compression Protocol") + HDRTOA(109, "snp", "Sitara Networks Protocol") + HDRTOA(110, "compaq-peer", "Compaq Peer Protocol") + HDRTOA(111, "ipx-in-ip", "IPX in IP") + HDRTOA(112, "vrrp", "Virtual Router Redundancy Protocol") + HDRTOA(113, "pgm", "PGM Reliable Transport Protocol") + HDRTOA(114, "any0hop", "any 0-hop protocol") + HDRTOA(115, "l2tp", "Layer Two Tunneling Protocol") + HDRTOA(116, "ddx", "D-II Data Exchange") + HDRTOA(117, "iatp", "Interactive Agent Transfer Protocol") + HDRTOA(118, "stp", "Schedule Transfer Protocol") + HDRTOA(119, "srp", "SpectraLink Radio Protocol") + HDRTOA(120, "uti", "UTI") + HDRTOA(121, "smp", "Simple Message Protocol") + HDRTOA(122, "sm", "Simple Multicast Protocol") + HDRTOA(123, "ptp", "Performance Transparency Protocol") + HDRTOA(124, "isis-ipv4", "ISIS over IPv4") + HDRTOA(125, "fire", "fire") + HDRTOA(126, "crtp", "Combat Radio Transport Protocol") + HDRTOA(127, "crudp", "Combat Radio User Datagram") + HDRTOA(128, "sscopmce", "sscopmce") + HDRTOA(129, "iplt", "iplt") + HDRTOA(130, "sps", "Secure Packet Shield") + HDRTOA(131, "pipe", "Private IP Encapsulation within IP") + HDRTOA(132, "sctp", "Stream Control Transmission Protocol") + HDRTOA(133, "fc", "Fibre Channel") + HDRTOA(134, "rsvp-e2e-ignore", "rsvp-e2e-ignore") + HDRTOA(135, "mobility-hdr", "Mobility Header") + HDRTOA(136, "udplite", "UDP-Lite [RFC3828]") + HDRTOA(137, "mpls-in-ip", "MPLS-in-IP [RFC4023]") + HDRTOA(138, "manet", "MANET Protocols [RFC5498]") + HDRTOA(139, "hip", "Host Identity Protocol") + HDRTOA(140, "shim6", "Shim6 Protocol [RFC5533]") + HDRTOA(141, "wesp", "Wrapped Encapsulating Security Payload") + HDRTOA(142, "rohc", "Robust Header Compression") + HDRTOA(143, "ethernet", "RFC 8986 Ethernet next-header") + HDRTOA(144, "aggfrag", "AGGFRAG encapsulation payload for ESP [draft-ietf-ipsecme-iptfs-18]") + HDRTOA(253, "experimental1", "Use for experimentation and testing") + HDRTOA(254, "experimental2", "Use for experimentation and testing") + default: + strncpy(buffer, acronym ? "unknown" : "Unknown protocol", 128);\ + break; + + } /* End of switch */ + + + return buffer; + +} /* End of nexthdrtoa() */ + + +/* TODO: Needs refactoring */ +static inline char* STRAPP(const char *fmt, ...) { + static char buf[256]; + static int bp; + int left = (int)sizeof(buf)-bp; + if(!fmt){ + bp = 0; + return(buf); + } + if (left <= 0) + return buf; + va_list ap; + va_start(ap, fmt); + bp += Vsnprintf (buf+bp, left, fmt, ap); + va_end(ap); + + return(buf); +} + +/* TODO: Needs refactoring */ +#define HEXDUMP -2 +#define UNKNOWN -1 + +#define BREAK() \ + {option_type = HEXDUMP; break;} +#define CHECK(tt) \ + if(tt >= option_end) \ + {option_type = HEXDUMP; break;} + +/* Takes binary data found in the IP Options field of an IPv4 packet + * and returns a string containing an ASCII description of the options + * found. The function returns a pointer to a static buffer that + * subsequent calls will overwrite. On error, NULL is returned. */ +char *format_ip_options(const u8* ipopt, int ipoptlen) { + char ipstring[32]; + int option_type = UNKNOWN;// option type + int option_len = 0; // option length + int option_pt = 0; // option pointer + int option_fl = 0; // option flag + const u8 *tptr; // temp pointer + u32 *tint; // temp int + + int option_sta = 0; // option start offset + int option_end = 0; // option end offset + int pt = 0; // current offset + + // clear buffer + STRAPP(NULL,NULL); + + if(!ipoptlen) + return(NULL); + + while(pt<ipoptlen){ // for every char in ipopt + // read ip option header + if(option_type == UNKNOWN) { + option_sta = pt; + option_type = ipopt[pt++]; + if(option_type != 0 && option_type != 1) { // should we be interested in length field? + if(pt >= ipoptlen) // no more chars + {option_type = HEXDUMP;pt--; option_end = 255; continue;} // no length field, hex dump to the end + option_len = ipopt[pt++]; + // end must not be greater than length + option_end = MIN(option_sta + option_len, ipoptlen); + // end must not be smaller than current position + option_end = MAX(option_end, option_sta+2); + } + } + switch(option_type) { + case 0: // IPOPT_END + STRAPP(" EOL", NULL); + option_type = UNKNOWN; + break; + case 1: // IPOPT_NOP + STRAPP(" NOP", NULL); + option_type = UNKNOWN; + break; +/* case 130: // IPOPT_SECURITY + option_type=-1; + break;*/ + case 131: // IPOPT_LSRR -> Loose Source and Record Route + case 137: // IPOPT_SSRR -> Strict Source and Record Route + case 7: // IPOPT_RR -> Record Route + if(pt - option_sta == 2) { + STRAPP(" %s%s{", (option_type==131)?"LS":(option_type==137)?"SS":"", "RR"); + // option pointer + CHECK(pt); + option_pt = ipopt[pt++]; + if(option_pt%4 != 0 || (option_sta + option_pt-1)>option_end || option_pt<4) //bad or too big pointer + STRAPP(" [bad ptr=%02i]", option_pt); + } + if(pt - option_sta > 2) { // ip's + int i, s = (option_pt)%4; + // if pointer is mangled, fix it. it's max 3 bytes wrong + CHECK(pt+3); + for(i=0; i<s; i++) + STRAPP("\\x%02x", ipopt[pt++]); + option_pt -= i; + // okay, now we can start printing ip's + CHECK(pt+3); + tptr = &ipopt[pt]; pt+=4; + if(inet_ntop(AF_INET, (char *) tptr, ipstring, sizeof(ipstring)) == NULL){ + return NULL; + } + STRAPP("%c%s",(pt-3-option_sta)==option_pt?'#':' ', ipstring); + if(pt == option_end) + STRAPP("%s",(pt-option_sta)==(option_pt-1)?"#":""); // pointer in the end? + }else BREAK(); + break; + case 68: // IPOPT_TS -> Internet Timestamp + if(pt - option_sta == 2){ + STRAPP(" TM{"); + // pointer + CHECK(pt); + option_pt = ipopt[pt++]; + // bad or too big pointer + if(option_pt%4 != 1 || (option_sta + option_pt-1)>option_end || option_pt<5) + STRAPP(" [bad ptr=%02i]", option_pt); + // flags + overflow + CHECK(pt); + option_fl = ipopt[pt++]; + if((option_fl&0x0C) || (option_fl&0x03)==2) + STRAPP(" [bad flags=\\x%01hhx]", option_fl&0x0F); + STRAPP("[%i hosts not recorded]", option_fl>>4); + option_fl &= 0x03; + } + if(pt - option_sta > 2) {// ip's + int i, s = (option_pt+3)%(option_fl==0?4:8); + // if pointer is mangled, fix it. it's max 3 bytes wrong + CHECK(pt+(option_fl==0?3:7)); + for(i=0; i<s; i++) + STRAPP("\\x%02x", ipopt[pt++]); + option_pt-=i; + + // print pt + STRAPP("%c",(pt+1-option_sta)==option_pt?'#':' '); + // okay, first grab ip. + if(option_fl!=0){ + CHECK(pt+3); + tptr = &ipopt[pt]; pt+=4; + if(inet_ntop(AF_INET, (char *) tptr, ipstring, sizeof(ipstring)) == NULL){ + return NULL; + } + STRAPP("%s@", ipstring); + } + CHECK(pt+3); + tint = (u32*)&ipopt[pt]; pt+=4; + STRAPP("%lu", (unsigned long) ntohl(*tint)); + + if(pt == option_end) + STRAPP("%s",(pt-option_sta)==(option_pt-1)?"#":" "); + }else BREAK(); + break; + case 136: // IPOPT_SATID -> (SANET) Stream Identifier + if(pt - option_sta == 2){ + u16 *sh; + STRAPP(" SI{",NULL); + // length + if(option_sta+option_len > ipoptlen || option_len!=4) + STRAPP("[bad len %02i]", option_len); + + // stream id + CHECK(pt+1); + sh = (u16*) &ipopt[pt]; pt+=2; + option_pt = ntohs(*sh); + STRAPP("id=%hu", (unsigned short) option_pt); + if(pt != option_end) + BREAK(); + }else BREAK(); + break; + case UNKNOWN: + default: + // we read option_type and option_len, print them. + STRAPP(" ??{\\x%02hhx\\x%02hhx", option_type, option_len); + // check option_end once more: + if(option_len < ipoptlen) + option_end = MIN(MAX(option_sta+option_len, option_sta+2),ipoptlen); + else + option_end = 255; + option_type = HEXDUMP; + break; + case HEXDUMP: + assert(pt<=option_end); + if(pt == option_end){ + STRAPP("}",NULL); + option_type=-1; + break; + } + STRAPP("\\x%02hhx", ipopt[pt++]); + break; + } + if(pt == option_end && option_type != UNKNOWN) { + STRAPP("}",NULL); + option_type = UNKNOWN; + } + } // while + if(option_type != UNKNOWN) + STRAPP("}"); + + return(STRAPP("",NULL)); +} +#undef CHECK +#undef BREAK +#undef UNKNOWN +#undef HEXDUMP + + + +/* Returns a buffer of ASCII information about an IP packet that may + * look like "TCP 127.0.0.1:50923 > 127.0.0.1:3 S ttl=61 id=39516 + * iplen=40 seq=625950769" or "ICMP PING (0/1) ttl=61 id=39516 iplen=40". + * Returned buffer is static so it is NOT safe to call this in + * multi-threaded environments without appropriate sync protection, or + * call it twice in the same sentence (eg: as two printf parameters). + * Obviously, the caller should never attempt to free() the buffer. The + * returned buffer is guaranteed to be NULL-terminated but no + * assumptions should be made concerning its length. + * + * The function knows IPv4, IPv6, TCP, UDP, SCTP, ICMP, and ICMPv6. + * + * The output has three different levels of detail. Parameter "detail" + * determines how verbose the output should be. It should take one of + * the following values: + * + * LOW_DETAIL (0x01): Traditional output. + * MEDIUM_DETAIL (0x02): More verbose than traditional. + * HIGH_DETAIL (0x03): Contents of virtually every field of the + * protocol headers . + */ +const char *ippackethdrinfo(const u8 *packet, u32 len, int detail) { + struct abstract_ip_hdr hdr; + const u8 *data; + unsigned int datalen; + + struct tcp_hdr *tcp = NULL; /* TCP header structure. */ + struct udp_hdr *udp = NULL; /* UDP header structure. */ + struct sctp_hdr *sctp = NULL; /* SCTP header structure. */ + static char protoinfo[1024] = ""; /* Stores final info string. */ + char ipinfo[512] = ""; /* Temp info about IP. */ + char icmpinfo[512] = ""; /* Temp info about ICMP. */ + char icmptype[128] = ""; /* Temp info about ICMP type & code */ + char icmpfields[256] = ""; /* Temp info for various ICMP fields */ + char fragnfo[64] = ""; /* Temp info about fragmentation. */ + char srchost[INET6_ADDRSTRLEN] = ""; /* Src IP in dot-decimal notation. */ + char dsthost[INET6_ADDRSTRLEN] = ""; /* Dst IP in dot-decimal notation. */ + char *p = NULL; /* Aux pointer. */ + int frag_off = 0; /* To compute IP fragment offset. */ + int more_fragments = 0; /* True if IP MF flag is set. */ + int dont_fragment = 0; /* True if IP DF flag is set. */ + int reserved_flag = 0; /* True if IP Reserved flag is set. */ + + datalen = len; + data = (u8 *) ip_get_data_any(packet, &datalen, &hdr); + if (data == NULL) + return "BOGUS! Can't parse supposed IP packet"; + + + /* Ensure we end up with a valid detail number */ + if (detail != LOW_DETAIL && detail != MEDIUM_DETAIL && detail != HIGH_DETAIL) + detail = LOW_DETAIL; + + /* IP INFORMATION ************************************************************/ + if (hdr.version == 4) { /* IPv4 */ + const struct ip *ip; + const struct sockaddr_in *sin; + + ip = (struct ip *) packet; + + /* Obtain IP source and destination info */ + sin = (struct sockaddr_in *) &hdr.src; + inet_ntop(AF_INET, (void *)&sin->sin_addr.s_addr, srchost, sizeof(srchost)); + sin = (struct sockaddr_in *) &hdr.dst; + inet_ntop(AF_INET, (void *)&sin->sin_addr.s_addr, dsthost, sizeof(dsthost)); + + /* Compute fragment offset and check if flags are set */ + frag_off = 8 * (ntohs(ip->ip_off) & 8191) /* 2^13 - 1 */; + more_fragments = ntohs(ip->ip_off) & IP_MF; + dont_fragment = ntohs(ip->ip_off) & IP_DF; + reserved_flag = ntohs(ip->ip_off) & IP_RF; + + /* Is this a fragmented packet? is it the last fragment? */ + if (frag_off || more_fragments) { + Snprintf(fragnfo, sizeof(fragnfo), " frag offset=%d%s", frag_off, more_fragments ? "+" : ""); + } + + /* Create a string with information relevant to the specified level of detail */ + if (detail == LOW_DETAIL) { + Snprintf(ipinfo, sizeof(ipinfo), "ttl=%d id=%hu iplen=%hu%s %s%s%s", + ip->ip_ttl, (unsigned short) ntohs(ip->ip_id), (unsigned short) ntohs(ip->ip_len), fragnfo, + ip->ip_hl==5?"":"ipopts={", + ip->ip_hl==5?"":format_ip_options((u8*) ip + sizeof(struct ip), MIN((unsigned)(ip->ip_hl-5)*4,len-sizeof(struct ip))), + ip->ip_hl==5?"":"}"); + } else if (detail == MEDIUM_DETAIL) { + Snprintf(ipinfo, sizeof(ipinfo), "ttl=%d id=%hu proto=%d csum=0x%04x iplen=%hu%s %s%s%s", + ip->ip_ttl, (unsigned short) ntohs(ip->ip_id), + ip->ip_p, ntohs(ip->ip_sum), + (unsigned short) ntohs(ip->ip_len), fragnfo, + ip->ip_hl==5?"":"ipopts={", + ip->ip_hl==5?"":format_ip_options((u8*) ip + sizeof(struct ip), MIN((unsigned)(ip->ip_hl-5)*4,len-sizeof(struct ip))), + ip->ip_hl==5?"":"}"); + } else if (detail == HIGH_DETAIL) { + Snprintf(ipinfo, sizeof(ipinfo), "ver=%d ihl=%d tos=0x%02x iplen=%hu id=%hu%s%s%s%s foff=%d%s ttl=%d proto=%d csum=0x%04x%s%s%s", + ip->ip_v, ip->ip_hl, + ip->ip_tos, (unsigned short) ntohs(ip->ip_len), + (unsigned short) ntohs(ip->ip_id), + (reserved_flag||dont_fragment||more_fragments) ? " flg=" : "", + (reserved_flag)? "x" : "", + (dont_fragment)? "D" : "", + (more_fragments)? "M": "", + frag_off, (more_fragments) ? "+" : "", + ip->ip_ttl, ip->ip_p, + ntohs(ip->ip_sum), + ip->ip_hl==5?"":" ipopts={", + ip->ip_hl==5?"":format_ip_options((u8*) ip + sizeof(struct ip), MIN((unsigned)(ip->ip_hl-5)*4,len-sizeof(struct ip))), + ip->ip_hl==5?"":"}"); + } + } else { /* IPv6 */ + const struct ip6_hdr *ip6; + const struct sockaddr_in6 *sin6; + + ip6 = (struct ip6_hdr *) packet; + + /* Obtain IP source and destination info */ + sin6 = (struct sockaddr_in6 *) &hdr.src; + inet_ntop(AF_INET6, (void *)sin6->sin6_addr.s6_addr, srchost, sizeof(srchost)); + sin6 = (struct sockaddr_in6 *) &hdr.dst; + inet_ntop(AF_INET6, (void *)sin6->sin6_addr.s6_addr, dsthost, sizeof(dsthost)); + + /* Obtain flow label and traffic class */ + u32 flow = ntohl(ip6->ip6_flow); + u32 ip6_fl = flow & 0x000fffff; + u32 ip6_tc = (flow & 0x0ff00000) >> 20; + + /* Create a string with information relevant to the specified level of detail */ + if (detail == LOW_DETAIL) { + Snprintf(ipinfo, sizeof(ipinfo), "hopl=%d flow=%x payloadlen=%hu", + ip6->ip6_hlim, ip6_fl, (unsigned short) ntohs(ip6->ip6_plen)); + } else if (detail == MEDIUM_DETAIL) { + Snprintf(ipinfo, sizeof(ipinfo), "hopl=%d tclass=%d flow=%x payloadlen=%hu", + ip6->ip6_hlim, ip6_tc, ip6_fl, (unsigned short) ntohs(ip6->ip6_plen)); + } else if (detail==HIGH_DETAIL) { + Snprintf(ipinfo, sizeof(ipinfo), "ver=6, tclass=%x flow=%x payloadlen=%hu nh=%s hopl=%d ", + ip6_tc, ip6_fl, (unsigned short) ntohs(ip6->ip6_plen), + nexthdrtoa(ip6->ip6_nxt, 1), ip6->ip6_hlim); + } + } + + + /* TCP INFORMATION ***********************************************************/ + if (hdr.proto == IPPROTO_TCP) { + char tflags[10]; + char tcpinfo[64] = ""; + char buf[32]; + char tcpoptinfo[256] = ""; + tcp = (struct tcp_hdr *) data; + + /* Let's parse the TCP header. The following code is very ugly because we + * have to deal with a lot of different situations. We don't want to + * segfault so we have to check every length and every bound to ensure we + * don't read past the packet. We cannot even trust the contents of the + * received packet because, for example, an IPv4 header may state it + * carries a TCP packet but may actually carry nothing at all. + * + * So we distinguish 4 situations. I know the first two are weird but they + * were there when I modified this code so I left them there just in + * case. + * 1. IP datagram is very small or is a fragment where we are missing + * the first part of the TCP header + * 2. IP datagram is a fragment and although we are missing the first + * 8 bytes of the TCP header, we have the rest of it (or some of + * the rest of it) + * 3. IP datagram is NOT a fragment but we don't have the full TCP + * header, we are missing some bytes. + * 4. IP datagram is NOT a fragment and we have at least a full 20 + * byte TCP header. + */ + + /* CASE 1: where we don't have the first 8 bytes of the TCP header because + * either the fragment belongs to somewhere past that or the IP contains + * less than 8 bytes. This also includes empty IP packets that say they + * contain a TCP packet. */ + if (frag_off > 8 || datalen < 8) { + Snprintf(protoinfo, sizeof(protoinfo), "TCP %s:?? > %s:?? ?? %s (incomplete)", + srchost, dsthost, ipinfo); + } + /* For all cases after this, datalen is necessarily >= 8 and frag_off is <= 8 */ + + /* CASE 2: where we are missing the first 8 bytes of the TCP header but we + * have, at least, the next 8 bytes so we can see the ACK number, the + * flags and window size. */ + else if (frag_off > 0) { + /* Fragmentation is on 8-byte boundaries, so 8 is the only legal value here. */ + assert(frag_off == 8); + tcp = (struct tcp_hdr *)((u8 *) tcp - frag_off); // ugly? + + /* TCP Flags */ + p = tflags; + /* These are basically in tcpdump order */ + if (tcp->th_flags & TH_SYN) + *p++ = 'S'; + if (tcp->th_flags & TH_FIN) + *p++ = 'F'; + if (tcp->th_flags & TH_RST) + *p++ = 'R'; + if (tcp->th_flags & TH_PUSH) + *p++ = 'P'; + if (tcp->th_flags & TH_ACK) { + *p++ = 'A'; + Snprintf(tcpinfo, sizeof(tcpinfo), " ack=%lu", + (unsigned long) ntohl(tcp->th_ack)); + } + if (tcp->th_flags & TH_URG) + *p++ = 'U'; + if (tcp->th_flags & TH_ECE) + *p++ = 'E'; /* rfc 2481/3168 */ + if (tcp->th_flags & TH_CWR) + *p++ = 'C'; /* rfc 2481/3168 */ + *p++ = '\0'; + + /* TCP Options */ + if ((u32) tcp->th_off * 4 > sizeof(struct tcp_hdr)) { + if (datalen < (u32) tcp->th_off * 4 - frag_off) { + Snprintf(tcpoptinfo, sizeof(tcpoptinfo), "option incomplete"); + } else { + tcppacketoptinfo((u8*) tcp + sizeof(struct tcp_hdr), + tcp->th_off*4 - sizeof(struct tcp_hdr), + tcpoptinfo, sizeof(tcpoptinfo)); + } + } + + /* Create a string with TCP information relevant to the specified level of detail */ + if (detail == LOW_DETAIL) { Snprintf(protoinfo, sizeof(protoinfo), "TCP %s:?? > %s:?? %s %s %s %s", + srchost, dsthost, tflags, ipinfo, tcpinfo, tcpoptinfo); + } else if (detail == MEDIUM_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "TCP %s:?? > %s:?? %s ack=%lu win=%hu %s IP [%s]", + srchost, dsthost, tflags, + (unsigned long) ntohl(tcp->th_ack), (unsigned short) ntohs(tcp->th_win), + tcpoptinfo, ipinfo); + } else if (detail == HIGH_DETAIL) { + if (datalen >= 12) { /* We have at least bytes 8-20 */ + Snprintf(protoinfo, sizeof(protoinfo), "TCP [%s:?? > %s:?? %s seq=%lu ack=%lu off=%d res=%d win=%hu csum=0x%04X urp=%hu%s%s] IP [%s]", + srchost, dsthost, tflags, + (unsigned long) ntohl(tcp->th_seq), + (unsigned long) ntohl(tcp->th_ack), + (u8)tcp->th_off, (u8)tcp->th_x2, (unsigned short) ntohs(tcp->th_win), + ntohs(tcp->th_sum), (unsigned short) ntohs(tcp->th_urp), + (tcpoptinfo[0]!='\0') ? " " : "", + tcpoptinfo, ipinfo); + } else { /* We only have bytes 8-16 */ + Snprintf(protoinfo, sizeof(protoinfo), "TCP %s:?? > %s:?? %s ack=%lu win=%hu %s IP [%s]", + srchost, dsthost, tflags, + (unsigned long) ntohl(tcp->th_ack), (unsigned short) ntohs(tcp->th_win), + tcpoptinfo, ipinfo); + } + } + } + /* For all cases after this, frag_off is necessarily 0 */ + + /* CASE 3: where the IP packet is not a fragment but for some reason, we + * don't have the entire TCP header, just part of it.*/ + else if (datalen < 20) { + /* We know we have the first 8 bytes, so what's left? */ + /* We only have the first 64 bits: ports and seq number */ + if (datalen < 12) { + Snprintf(tcpinfo, sizeof(tcpinfo), "TCP %s:%hu > %s:%hu ?? seq=%lu (incomplete) %s", + srchost, (unsigned short) ntohs(tcp->th_sport), dsthost, + (unsigned short) ntohs(tcp->th_dport), (unsigned long) ntohl(tcp->th_seq), ipinfo); + } + + /* We only have the first 96 bits: ports, seq and ack number */ + else if (datalen < 16) { + if (detail == LOW_DETAIL) { /* We don't print ACK in low detail */ + Snprintf(tcpinfo, sizeof(tcpinfo), "TCP %s:%hu > %s:%hu seq=%lu (incomplete), %s", + srchost, (unsigned short) ntohs(tcp->th_sport), dsthost, + (unsigned short) ntohs(tcp->th_dport), (unsigned long) ntohl(tcp->th_seq), ipinfo); + } else { + Snprintf(tcpinfo, sizeof(tcpinfo), "TCP [%s:%hu > %s:%hu seq=%lu ack=%lu (incomplete)] IP [%s]", + srchost, (unsigned short) ntohs(tcp->th_sport), dsthost, + (unsigned short) ntohs(tcp->th_dport), (unsigned long) ntohl(tcp->th_seq), + (unsigned long) ntohl(tcp->th_ack), ipinfo); + } + } + + /* We are missing some part of the last 32 bits (checksum and urgent pointer) */ + else { + p = tflags; + /* These are basically in tcpdump order */ + if (tcp->th_flags & TH_SYN) + *p++ = 'S'; + if (tcp->th_flags & TH_FIN) + *p++ = 'F'; + if (tcp->th_flags & TH_RST) + *p++ = 'R'; + if (tcp->th_flags & TH_PUSH) + *p++ = 'P'; + if (tcp->th_flags & TH_ACK) { + *p++ = 'A'; + Snprintf(buf, sizeof(buf), " ack=%lu", + (unsigned long) ntohl(tcp->th_ack)); + strncat(tcpinfo, buf, sizeof(tcpinfo) - strlen(tcpinfo) - 1); + } + if (tcp->th_flags & TH_URG) + *p++ = 'U'; + if (tcp->th_flags & TH_ECE) + *p++ = 'E'; /* rfc 2481/3168 */ + if (tcp->th_flags & TH_CWR) + *p++ = 'C'; /* rfc 2481/3168 */ + *p++ = '\0'; + + + /* Create a string with TCP information relevant to the specified level of detail */ + if (detail == LOW_DETAIL) { /* We don't print ACK in low detail */ + Snprintf(protoinfo, sizeof(protoinfo), "TCP %s:%hu > %s:%hu %s %s seq=%lu win=%hu (incomplete)", + srchost, (unsigned short) ntohs(tcp->th_sport), dsthost, (unsigned short) ntohs(tcp->th_dport), + tflags, ipinfo, (unsigned long) ntohl(tcp->th_seq), + (unsigned short) ntohs(tcp->th_win)); + } else if (detail == MEDIUM_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "TCP [%s:%hu > %s:%hu %s seq=%lu ack=%lu win=%hu (incomplete)] IP [%s]", + srchost, (unsigned short) ntohs(tcp->th_sport), dsthost, (unsigned short) ntohs(tcp->th_dport), + tflags, (unsigned long) ntohl(tcp->th_seq), + (unsigned long) ntohl(tcp->th_ack), + (unsigned short) ntohs(tcp->th_win), ipinfo); + } else if (detail == HIGH_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "TCP [%s:%hu > %s:%hu %s seq=%lu ack=%lu off=%d res=%d win=%hu (incomplete)] IP [%s]", + srchost, (unsigned short) ntohs(tcp->th_sport), + dsthost, (unsigned short) ntohs(tcp->th_dport), + tflags, (unsigned long) ntohl(tcp->th_seq), + (unsigned long) ntohl(tcp->th_ack), + (u8)tcp->th_off, (u8)tcp->th_x2, (unsigned short) ntohs(tcp->th_win), + ipinfo); + } + } + } + + /* CASE 4: where we (finally!) have a full 20 byte TCP header so we can + * safely print all fields */ + else { /* if (datalen >= 20) */ + + /* TCP Flags */ + p = tflags; + /* These are basically in tcpdump order */ + if (tcp->th_flags & TH_SYN) + *p++ = 'S'; + if (tcp->th_flags & TH_FIN) + *p++ = 'F'; + if (tcp->th_flags & TH_RST) + *p++ = 'R'; + if (tcp->th_flags & TH_PUSH) + *p++ = 'P'; + if (tcp->th_flags & TH_ACK) { + *p++ = 'A'; + Snprintf(buf, sizeof(buf), " ack=%lu", + (unsigned long) ntohl(tcp->th_ack)); + strncat(tcpinfo, buf, sizeof(tcpinfo) - strlen(tcpinfo) - 1); + } + if (tcp->th_flags & TH_URG) + *p++ = 'U'; + if (tcp->th_flags & TH_ECE) + *p++ = 'E'; /* rfc 2481/3168 */ + if (tcp->th_flags & TH_CWR) + *p++ = 'C'; /* rfc 2481/3168 */ + *p++ = '\0'; + + /* TCP Options */ + if ((u32) tcp->th_off * 4 > sizeof(struct tcp_hdr)) { + if (datalen < (unsigned int) tcp->th_off * 4) { + Snprintf(tcpoptinfo, sizeof(tcpoptinfo), "option incomplete"); + } else { + tcppacketoptinfo((u8*) tcp + sizeof(struct tcp_hdr), + tcp->th_off*4 - sizeof(struct tcp_hdr), + tcpoptinfo, sizeof(tcpoptinfo)); + } + } + + /* Rest of header fields */ + if (detail == LOW_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "TCP %s:%hu > %s:%hu %s %s seq=%lu win=%hu %s", + srchost, (unsigned short) ntohs(tcp->th_sport), dsthost, (unsigned short) ntohs(tcp->th_dport), + tflags, ipinfo, (unsigned long) ntohl(tcp->th_seq), + (unsigned short) ntohs(tcp->th_win), tcpoptinfo); + } else if (detail == MEDIUM_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "TCP [%s:%hu > %s:%hu %s seq=%lu win=%hu csum=0x%04X%s%s] IP [%s]", + srchost, (unsigned short) ntohs(tcp->th_sport), dsthost, (unsigned short) ntohs(tcp->th_dport), + tflags, (unsigned long) ntohl(tcp->th_seq), + (unsigned short) ntohs(tcp->th_win), (unsigned short) ntohs(tcp->th_sum), + (tcpoptinfo[0]!='\0') ? " " : "", + tcpoptinfo, ipinfo); + } else if (detail == HIGH_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "TCP [%s:%hu > %s:%hu %s seq=%lu ack=%lu off=%d res=%d win=%hu csum=0x%04X urp=%hu%s%s] IP [%s]", + srchost, (unsigned short) ntohs(tcp->th_sport), + dsthost, (unsigned short) ntohs(tcp->th_dport), + tflags, (unsigned long) ntohl(tcp->th_seq), + (unsigned long) ntohl(tcp->th_ack), + (u8)tcp->th_off, (u8)tcp->th_x2, (unsigned short) ntohs(tcp->th_win), + ntohs(tcp->th_sum), (unsigned short) ntohs(tcp->th_urp), + (tcpoptinfo[0]!='\0') ? " " : "", + tcpoptinfo, ipinfo); + } + } + + /* UDP INFORMATION ***********************************************************/ + } else if (hdr.proto == IPPROTO_UDP && frag_off) { + Snprintf(protoinfo, sizeof(protoinfo), "UDP %s:?? > %s:?? fragment %s (incomplete)", + srchost, dsthost, ipinfo); + } else if (hdr.proto == IPPROTO_UDP) { + udp = (struct udp_hdr *) data; + /* TODO: See if we can segfault if we receive a fragmented packet whose IP packet does not say a thing about fragmentation */ + + if (detail == LOW_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "UDP %s:%hu > %s:%hu %s", + srchost, (unsigned short) ntohs(udp->uh_sport), dsthost, (unsigned short) ntohs(udp->uh_dport), + ipinfo); + } else if (detail == MEDIUM_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "UDP [%s:%hu > %s:%hu csum=0x%04X] IP [%s]", + srchost, (unsigned short) ntohs(udp->uh_sport), dsthost, (unsigned short) ntohs(udp->uh_dport), ntohs(udp->uh_sum), + ipinfo); + } else if (detail == HIGH_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "UDP [%s:%hu > %s:%hu len=%hu csum=0x%04X] IP [%s]", + srchost, (unsigned short) ntohs(udp->uh_sport), dsthost, (unsigned short) ntohs(udp->uh_dport), + (unsigned short) ntohs(udp->uh_ulen), ntohs(udp->uh_sum), + ipinfo); + } + + /* SCTP INFORMATION **********************************************************/ + } else if (hdr.proto == IPPROTO_SCTP && frag_off) { + Snprintf(protoinfo, sizeof(protoinfo), "SCTP %s:?? > %s:?? fragment %s (incomplete)", + srchost, dsthost, ipinfo); + } else if (hdr.proto == IPPROTO_SCTP) { + sctp = (struct sctp_hdr *) data; + + if (detail == LOW_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "SCTP %s:%hu > %s:%hu %s", + srchost, (unsigned short) ntohs(sctp->sh_sport), dsthost, (unsigned short) ntohs(sctp->sh_dport), + ipinfo); + } else if (detail == MEDIUM_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "SCTP [%s:%hu > %s:%hu csum=0x%08x] IP [%s]", + srchost, (unsigned short) ntohs(sctp->sh_sport), dsthost, (unsigned short) ntohs(sctp->sh_dport), ntohl(sctp->sh_sum), + ipinfo); + } else if (detail == HIGH_DETAIL) { + Snprintf(protoinfo, sizeof(protoinfo), "SCTP [%s:%hu > %s:%hu vtag=%lu csum=0x%08x] IP [%s]", + srchost, (unsigned short) ntohs(sctp->sh_sport), dsthost, (unsigned short) ntohs(sctp->sh_dport), + (unsigned long) ntohl(sctp->sh_vtag), ntohl(sctp->sh_sum), + ipinfo); + } + + /* ICMP INFORMATION **********************************************************/ + } else if (hdr.proto == IPPROTO_ICMP && frag_off) { + Snprintf(protoinfo, sizeof(protoinfo), "ICMP %s > %s fragment %s (incomplete)", + srchost, dsthost, ipinfo); + } else if (hdr.proto == IPPROTO_ICMP) { + struct ip *ip2; /* Points to the IP datagram carried by some ICMP messages */ + char *ip2dst; /* Dest IP in caried IP datagram */ + char auxbuff[128]; /* Aux buffer */ + struct icmp_packet{ /* Generic ICMP struct */ + u8 type; + u8 code; + u16 checksum; + u8 data[128]; + }*icmppkt; + struct ppkt { /* Beginning of ICMP Echo/Timestamp header */ + u8 type; + u8 code; + u16 checksum; + u16 id; + u16 seq; + } *ping = NULL; + struct icmp_redir{ + u8 type; + u8 code; + u16 checksum; + u32 addr; + } *icmpredir = NULL; + struct icmp_router{ + u8 type; + u8 code; + u16 checksum; + u8 addrs; + u8 addrlen; + u16 lifetime; + } *icmprouter = NULL; + struct icmp_param{ + u8 type; + u8 code; + u16 checksum; + u8 pnt; + u8 unused; + u16 unused2; + } *icmpparam = NULL; + struct icmp_tstamp{ + u8 type; + u8 code; + u16 checksum; + u16 id; + u16 seq; + u32 orig; + u32 recv; + u32 trans; + } *icmptstamp = NULL; + struct icmp_amask{ + u8 type; + u8 code; + u16 checksum; + u16 id; + u16 seq; + u32 mask; + } *icmpmask = NULL; + + /* Compute the ICMP minimum length. */ + unsigned pktlen = 8; + + /* We need the ICMP packet to be at least 8 bytes long */ + if (pktlen > datalen) + goto icmpbad; + + ping = (struct ppkt *) data; + icmppkt = (struct icmp_packet *) data; + + switch(icmppkt->type) { + /* Echo Reply **************************/ + case 0: + strcpy(icmptype, "Echo reply"); + Snprintf(icmpfields, sizeof(icmpfields), "id=%hu seq=%hu", (unsigned short) ntohs(ping->id), (unsigned short) ntohs(ping->seq)); + break; + + /* Destination Unreachable *************/ + case 3: + /* Point to the start of the original datagram */ + ip2 = (struct ip *) (data + 8); + + /* Check we have a full IP datagram included in the ICMP message */ + pktlen += MAX( (ip2->ip_hl * 4), 20); + if (pktlen > datalen) { + if (datalen == 8) { + Snprintf(icmptype, sizeof icmptype, "Destination unreachable%s", + (detail!=LOW_DETAIL)? " (original datagram missing)" : ""); + } else { + Snprintf(icmptype, sizeof icmptype, "Destination unreachable%s", + (detail!=LOW_DETAIL)? " (part of original datagram missing)" : ""); + } + goto icmpbad; + } + + /* Basic check to ensure we have an IPv4 datagram attached */ + /* TODO: We should actually check the datagram checksum to + * see if it validates because just checking the version number + * is not enough. On average, if we get random data 1 out of + * 16 (2^4bits) times we will have value 4. */ + if ((ip2->ip_v != 4) || ((ip2->ip_hl * 4) < 20) || ((ip2->ip_hl * 4) > 60)) { + Snprintf(icmptype, sizeof icmptype, "Destination unreachable (bogus original datagram)"); + goto icmpbad; + } else { + /* We have the original datagram + the first 8 bytes of the + * transport layer header */ + if (pktlen + 8 < datalen) { + tcp = (struct tcp_hdr *) ((char *) ip2 + (ip2->ip_hl * 4)); + udp = (struct udp_hdr *) ((char *) ip2 + (ip2->ip_hl * 4)); + sctp = (struct sctp_hdr *) ((char *) ip2 + (ip2->ip_hl * 4)); + } + } + + /* Determine the IP the original datagram was sent to */ + ip2dst = inet_ntoa(ip2->ip_dst); + + /* Determine type of Destination unreachable from the code value */ + switch (icmppkt->code) { + case 0: + Snprintf(icmptype, sizeof icmptype, "Network %s unreachable", ip2dst); + break; + + case 1: + Snprintf(icmptype, sizeof icmptype, "Host %s unreachable", ip2dst); + break; + + case 2: + Snprintf(icmptype, sizeof icmptype, "Protocol %u unreachable", ip2->ip_p); + break; + + case 3: + if (pktlen + 8 < datalen) { + if (ip2->ip_p == IPPROTO_UDP && udp) + Snprintf(icmptype, sizeof icmptype, "Port %hu unreachable", (unsigned short) ntohs(udp->uh_dport)); + else if (ip2->ip_p == IPPROTO_TCP && tcp) + Snprintf(icmptype, sizeof icmptype, "Port %hu unreachable", (unsigned short) ntohs(tcp->th_dport)); + else if (ip2->ip_p == IPPROTO_SCTP && sctp) + Snprintf(icmptype, sizeof icmptype, "Port %hu unreachable", (unsigned short) ntohs(sctp->sh_dport)); + else + Snprintf(icmptype, sizeof icmptype, "Port unreachable (unknown protocol %u)", ip2->ip_p); + } + else + strcpy(icmptype, "Port unreachable"); + break; + + case 4: + strcpy(icmptype, "Fragmentation required"); + Snprintf(icmpfields, sizeof(icmpfields), "Next-Hop-MTU=%d", icmppkt->data[2]<<8 | icmppkt->data[3]); + break; + + case 5: + strcpy(icmptype, "Source route failed"); + break; + + case 6: + Snprintf(icmptype, sizeof icmptype, "Destination network %s unknown", ip2dst); + break; + + case 7: + Snprintf(icmptype, sizeof icmptype, "Destination host %s unknown", ip2dst); + break; + + case 8: + strcpy(icmptype, "Source host isolated"); + break; + + case 9: + Snprintf(icmptype, sizeof icmptype, "Destination network %s administratively prohibited", ip2dst); + break; + + case 10: + Snprintf(icmptype, sizeof icmptype, "Destination host %s administratively prohibited", ip2dst); + break; + + case 11: + Snprintf(icmptype, sizeof icmptype, "Network %s unreachable for TOS", ip2dst); + break; + + case 12: + Snprintf(icmptype, sizeof icmptype, "Host %s unreachable for TOS", ip2dst); + break; + + case 13: + strcpy(icmptype, "Communication administratively prohibited by filtering"); + break; + + case 14: + strcpy(icmptype, "Host precedence violation"); + break; + + case 15: + strcpy(icmptype, "Precedence cutoff in effect"); + break; + + default: + strcpy(icmptype, "Destination unreachable (unknown code)"); + break; + } /* End of ICMP Code switch */ + break; + + + /* Source Quench ***********************/ + case 4: + strcpy(icmptype, "Source quench"); + break; + + /* Redirect ****************************/ + case 5: + if (ping->code == 0) + strcpy(icmptype, "Network redirect"); + else if (ping->code == 1) + strcpy(icmptype, "Host redirect"); + else + strcpy(icmptype, "Redirect (unknown code)"); + icmpredir = (struct icmp_redir *) icmppkt; + inet_ntop(AF_INET, &icmpredir->addr, auxbuff, sizeof(auxbuff)); + Snprintf(icmpfields, sizeof(icmpfields), "addr=%s", auxbuff); + break; + + /* Echo Request ************************/ + case 8: + strcpy(icmptype, "Echo request"); + Snprintf(icmpfields, sizeof(icmpfields), "id=%hu seq=%hu", (unsigned short) ntohs(ping->id), (unsigned short) ntohs(ping->seq)); + break; + + /* Router Advertisement ****************/ + case 9: + if (icmppkt->code == 16) + strcpy(icmptype, "Router advertisement (Mobile Agent Only)"); + else + strcpy(icmptype, "Router advertisement"); + icmprouter = (struct icmp_router *) icmppkt; + Snprintf(icmpfields, sizeof(icmpfields), "addrs=%u addrlen=%u lifetime=%hu", + icmprouter->addrs, + icmprouter->addrlen, + (unsigned short) ntohs(icmprouter->lifetime)); + break; + + /* Router Solicitation *****************/ + case 10: + strcpy(icmptype, "Router solicitation"); + break; + + /* Time Exceeded ***********************/ + case 11: + if (icmppkt->code == 0) + strcpy(icmptype, "TTL=0 during transit"); + else if (icmppkt->code == 1) + strcpy(icmptype, "TTL=0 during reassembly"); + else + strcpy(icmptype, "TTL exceeded (unknown code)"); + break; + + /* Parameter Problem *******************/ + case 12: + if (ping->code == 0) + strcpy(icmptype, "Parameter problem (pointer indicates error)"); + else if (ping->code == 1) + strcpy(icmptype, "Parameter problem (option missing)"); + else if (ping->code == 2) + strcpy(icmptype, "Parameter problem (bad length)"); + else + strcpy(icmptype, "Parameter problem (unknown code)"); + icmpparam = (struct icmp_param *) icmppkt; + Snprintf(icmpfields, sizeof(icmpfields), "pointer=%d", icmpparam->pnt); + break; + + /* Timestamp Request/Reply *************/ + case 13: + case 14: + Snprintf(icmptype, sizeof(icmptype), "Timestamp %s", (icmppkt->type == 13)? "request" : "reply"); + icmptstamp = (struct icmp_tstamp *) icmppkt; + Snprintf(icmpfields, sizeof(icmpfields), "id=%hu seq=%hu orig=%lu recv=%lu trans=%lu", + (unsigned short) ntohs(icmptstamp->id), (unsigned short) ntohs(icmptstamp->seq), + (unsigned long) ntohl(icmptstamp->orig), + (unsigned long) ntohl(icmptstamp->recv), + (unsigned long) ntohl(icmptstamp->trans)); + break; + + /* Information Request *****************/ + case 15: + strcpy(icmptype, "Information request"); + Snprintf(icmpfields, sizeof(icmpfields), "id=%hu seq=%hu", (unsigned short) ntohs(ping->id), (unsigned short) ntohs(ping->seq)); + break; + + /* Information Reply *******************/ + case 16: + strcpy(icmptype, "Information reply"); + Snprintf(icmpfields, sizeof(icmpfields), "id=%hu seq=%hu", (unsigned short) ntohs(ping->id), (unsigned short) ntohs(ping->seq)); + break; + + /* Netmask Request/Reply ***************/ + case 17: + case 18: + Snprintf(icmptype, sizeof(icmptype), "Address mask %s", (icmppkt->type == 17)? "request" : "reply"); + icmpmask = (struct icmp_amask *) icmppkt; + inet_ntop(AF_INET, &icmpmask->mask, auxbuff, sizeof(auxbuff)); + Snprintf(icmpfields, sizeof(icmpfields), "id=%u seq=%u mask=%s", + (unsigned short) ntohs(ping->id), (unsigned short) ntohs(ping->seq), auxbuff); + break; + + /* Traceroute **************************/ + case 30: + strcpy(icmptype, "Traceroute"); + break; + + /* Domain Name Request *****************/ + case 37: + strcpy(icmptype, "Domain name request"); + break; + + /* Domain Name Reply *******************/ + case 38: + strcpy(icmptype, "Domain name reply"); + break; + + /* Security ****************************/ + case 40: + strcpy(icmptype, "Security failures"); /* rfc 2521 */ + break; + + default: + strcpy(icmptype, "Unknown type"); break; + break; + } /* End of ICMP Type switch */ + + if (pktlen > datalen) { +icmpbad: + if (ping) { + /* We still have this information */ + Snprintf(protoinfo, sizeof(protoinfo), "ICMP %s > %s %s (type=%d/code=%d) %s", + srchost, dsthost, icmptype, ping->type, ping->code, ipinfo); + } else { + Snprintf(protoinfo, sizeof(protoinfo), "ICMP %s > %s [??] %s", + srchost, dsthost, ipinfo); + } + } else { + if (ping) + sprintf(icmpinfo,"type=%d/code=%d", ping->type, ping->code); + else + strncpy(icmpinfo,"type=?/code=?", sizeof(icmpinfo)); + + Snprintf(protoinfo, sizeof(protoinfo), "ICMP [%s > %s %s (%s) %s] IP [%s]", + srchost, dsthost, icmptype, icmpinfo, icmpfields, ipinfo); + } + + } else if (hdr.proto == IPPROTO_ICMPV6) { + if (datalen > sizeof(struct icmpv6_hdr)) { + const struct icmpv6_hdr *icmpv6; + + icmpv6 = (struct icmpv6_hdr *) data; + Snprintf(protoinfo, sizeof(protoinfo), "ICMPv6 (%d) %s > %s (type=%d/code=%d) %s", + hdr.proto, srchost, dsthost, + icmpv6->icmpv6_type, icmpv6->icmpv6_code, ipinfo); + } + else { + Snprintf(protoinfo, sizeof(protoinfo), "ICMPv6 (%d) %s > %s (type=?/code=?) %s", + hdr.proto, srchost, dsthost, ipinfo); + } + } else { + /* UNKNOWN PROTOCOL **********************************************************/ + const char *hdrstr; + + hdrstr = nexthdrtoa(hdr.proto, 1); + if (hdrstr == NULL || *hdrstr == '\0') { + Snprintf(protoinfo, sizeof(protoinfo), "Unknown protocol (%d) %s > %s: %s", + hdr.proto, srchost, dsthost, ipinfo); + } else { + Snprintf(protoinfo, sizeof(protoinfo), "%s (%d) %s > %s: %s", + hdrstr, hdr.proto, srchost, dsthost, ipinfo); + } + } + + return protoinfo; +} + + +#ifdef HAVE_LINUX_RTNETLINK_H +/* Fill in a sockaddr_storage given an address family and raw address. */ +static int set_sockaddr(struct sockaddr_storage *ss, int af, void *data) { + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + ss->ss_family = af; + if (af == AF_INET) { + sin = (struct sockaddr_in *) ss; + memcpy(&sin->sin_addr.s_addr, data, IP_ADDR_LEN); + } else if (af == AF_INET6) { + sin6 = (struct sockaddr_in6 *) ss; + memcpy(sin6->sin6_addr.s6_addr, data, IP6_ADDR_LEN); + } else { + return -1; + } + + return 0; +} + +/* Add rtattrs to a netlink message specifying a source or destination address. + rta_type must be RTA_SRC or RTA_DST. This function adds either 1 or 2 + rtattrs: it always adds either an RTA_SRC or RTA_DST, depending on rta_type. + If ifindex is not 0, it is the index of the interface to use. The function + adds either RTA_OIF if rta_type is RTA_DST, and either of ifindex and + sin6_scope_id is nonzero. */ +static void add_rtattr_addr(struct nlmsghdr *nlmsg, + struct rtattr **rtattr, unsigned int *len, + unsigned char rta_type, + const struct sockaddr_storage *ss, + int ifindex) { + struct rtmsg *rtmsg; + const void *addr; + size_t addrlen; + + assert(rta_type == RTA_SRC || rta_type == RTA_DST); + + if (rta_type == RTA_SRC) { + /* Ignore the interface specification if we are setting an RTA_SRC attribute + (it may still get set by the scope_id below). */ + ifindex = 0; + } + + if (ss->ss_family == AF_INET) { + addr = &((struct sockaddr_in *) ss)->sin_addr.s_addr; + addrlen = IP_ADDR_LEN; + } else if (ss->ss_family == AF_INET6) { + const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) ss; + + addr = sin6->sin6_addr.s6_addr; + addrlen = IP6_ADDR_LEN; + if (ifindex == 0) + ifindex = sin6->sin6_scope_id; + } else { + netutil_fatal("%s: unknown address family %d", __func__, ss->ss_family); + } + + rtmsg = (struct rtmsg *) (nlmsg + 1); + if (rta_type == RTA_SRC) + rtmsg->rtm_src_len = addrlen * 8; + else + rtmsg->rtm_dst_len = addrlen * 8; + + /* Add an rtattr for the address. */ + (*rtattr)->rta_type = rta_type; + (*rtattr)->rta_len = RTA_LENGTH(addrlen); + assert(RTA_OK(*rtattr, *len)); + memcpy(RTA_DATA(*rtattr), addr, addrlen); + nlmsg->nlmsg_len = NLMSG_ALIGN(nlmsg->nlmsg_len) + (*rtattr)->rta_len; + *rtattr = RTA_NEXT(*rtattr, *len); + + /* Specific interface (sin6_scope_id) requested? */ + if (ifindex > 0) { + /* Add an rtattr for the interface. */ + if (rta_type == RTA_SRC) + (*rtattr)->rta_type = RTA_IIF; + else + (*rtattr)->rta_type = RTA_OIF; + (*rtattr)->rta_len = RTA_LENGTH(sizeof(uint32_t)); + assert(RTA_OK(*rtattr, *len)); + *(uint32_t *) RTA_DATA(*rtattr) = ifindex; + nlmsg->nlmsg_len = NLMSG_ALIGN(nlmsg->nlmsg_len) + (*rtattr)->rta_len; + *rtattr = RTA_NEXT(*rtattr, *len); + } +} + +/* Does route_dst using the Linux-specific rtnetlink interface. See rtnetlink(3) + and rtnetlink(7). */ +static int route_dst_netlink(const struct sockaddr_storage *dst, + struct route_nfo *rnfo, const char *device, + const struct sockaddr_storage *spoofss) { + struct sockaddr_nl snl; + struct msghdr msg; + struct iovec iov; + struct nlmsghdr *nlmsg; + struct rtmsg *rtmsg; + struct rtattr *rtattr; + int intf_index; + unsigned char buf[512]; + unsigned int len; + int fd, rc; + + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (fd == -1) + netutil_fatal("%s: cannot create AF_NETLINK socket: %s", __func__, strerror(errno)); + + memset(&snl, 0, sizeof(snl)); + snl.nl_family = AF_NETLINK; + + rc = bind(fd, (struct sockaddr *) &snl, sizeof(snl)); + if (rc == -1) + netutil_fatal("%s: cannot bind AF_NETLINK socket: %s", __func__, strerror(errno)); + + struct interface_info *ii; + ii = NULL; + intf_index = 0; + if (device != NULL && device[0] != '\0') { + ii = getInterfaceByName(device, dst->ss_family); + if (ii == NULL) + netutil_fatal("Could not find interface %s which was specified by -e", device); + intf_index = ii->ifindex; + } + + memset(buf, 0, sizeof(buf)); + + nlmsg = (struct nlmsghdr *) buf; + + nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(*rtmsg)); + assert(nlmsg->nlmsg_len <= sizeof(buf)); + nlmsg->nlmsg_flags = NLM_F_REQUEST; + nlmsg->nlmsg_type = RTM_GETROUTE; + + rtmsg = (struct rtmsg *) (nlmsg + 1); + rtmsg->rtm_family = dst->ss_family; + + rtattr = RTM_RTA(rtmsg); + len = sizeof(buf) - ((unsigned char *) RTM_RTA(rtmsg) - buf); + + /* Add rtattrs for destination address and interface. */ + add_rtattr_addr(nlmsg, &rtattr, &len, RTA_DST, dst, intf_index); + if (spoofss != NULL) { + /* Add rtattrs for source address and interface. */ + add_rtattr_addr(nlmsg, &rtattr, &len, RTA_SRC, spoofss, intf_index); + } + + iov.iov_base = nlmsg; + iov.iov_len = nlmsg->nlmsg_len; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &snl; + msg.msg_namelen = sizeof(snl); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + rc = sendmsg(fd, &msg, 0); + if (rc == -1) + netutil_fatal("%s: cannot sendmsg: %s", __func__, strerror(errno)); + + iov.iov_base = buf; + iov.iov_len = sizeof(buf); + + len = recvmsg(fd, &msg, 0); + if (len <= 0) + netutil_fatal("%s: cannot recvmsg: %s", __func__, strerror(errno)); + + close(fd); + + if (nlmsg->nlmsg_len < sizeof(*nlmsg) || (unsigned int) len < NLMSG_LENGTH(sizeof(*nlmsg))) + netutil_fatal("%s: wrong size reply in recvmsg", __func__); + len -= NLMSG_LENGTH(sizeof(*nlmsg)); + + /* See rtnetlink(7). Anything matching this route is actually unroutable. */ + if (rtmsg->rtm_type == RTN_UNREACHABLE || rtmsg->rtm_type == RTN_UNSPEC + || rtmsg->rtm_type == RTN_BLACKHOLE || rtmsg->rtm_type == RTN_PROHIBIT) + return 0; + + /* Default values to be possibly overridden. */ + rnfo->direct_connect = 1; + rnfo->nexthop.ss_family = AF_UNSPEC; + rnfo->srcaddr.ss_family = AF_UNSPEC; + if (spoofss != NULL) + rnfo->srcaddr = *spoofss; + + for (rtattr = RTM_RTA(rtmsg); RTA_OK(rtattr, len); rtattr = RTA_NEXT(rtattr, len)) { + if (rtattr->rta_type == RTA_GATEWAY) { + rc = set_sockaddr(&rnfo->nexthop, dst->ss_family, RTA_DATA(rtattr)); + assert(rc != -1); + /* Don't consider it directly connected if nexthop != dst. */ + if (!sockaddr_storage_equal(dst, &rnfo->nexthop)) + rnfo->direct_connect = 0; + } else if (rtattr->rta_type == RTA_OIF && ii == NULL) { + char namebuf[IFNAMSIZ]; + char *p; + int intf_index; + + intf_index = *(int *) RTA_DATA(rtattr); + p = if_indextoname(intf_index, namebuf); + if (p == NULL) + netutil_fatal("%s: if_indextoname(%d) failed: %d (%s)", __func__, intf_index, errno, strerror(errno)); + ii = getInterfaceByName(namebuf, dst->ss_family); + if (ii == NULL) + ii = getInterfaceByName(namebuf, AF_UNSPEC); + if (ii == NULL) + netutil_fatal("%s: can't find interface \"%s\"", __func__, namebuf); + } else if (rtattr->rta_type == RTA_PREFSRC && rnfo->srcaddr.ss_family == AF_UNSPEC) { + rc = set_sockaddr(&rnfo->srcaddr, dst->ss_family, RTA_DATA(rtattr)); + assert(rc != -1); + } + } + + if (ii != NULL) { + rnfo->ii = *ii; + return 1; + } else { + return 0; + } +} + +#else + +static struct interface_info *find_loopback_iface(struct interface_info *ifaces, + int numifaces) { + int i; + + for (i = 0; i < numifaces; i++) { + if (ifaces[i].device_type == devt_loopback) + return &ifaces[i]; + } + + return NULL; +} + +/* Get the source address for routing to dst by creating a socket and asking the + operating system for the local address. */ +static int get_srcaddr(const struct sockaddr_storage *dst, + struct sockaddr_storage *src) +{ + static const unsigned short DUMMY_PORT = 1234; + struct sockaddr_storage dst_dummy; + size_t dst_dummy_len; + socklen_t len; + int fd, rc; + + fd = socket(dst->ss_family, SOCK_DGRAM, 0); + if (fd == -1) + netutil_fatal("%s: can't create socket: %s", __func__, socket_strerror(socket_errno())); + + dst_dummy = *dst; + if (dst_dummy.ss_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *) &dst_dummy; + sin->sin_port = htons(DUMMY_PORT); + dst_dummy_len = sizeof(*sin); + } else if (dst_dummy.ss_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &dst_dummy; + sin6->sin6_port = htons(DUMMY_PORT); + dst_dummy_len = sizeof(*sin6); + } else { + goto bail; + } + + rc = connect(fd, (struct sockaddr *) &dst_dummy, dst_dummy_len); + if (rc == -1) { + netutil_error("%s: can't connect socket: %s", __func__, socket_strerror(socket_errno())); + if (dst->ss_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &dst_dummy; + if (sin6->sin6_scope_id == 0) + netutil_error("Do you need an IPv6 zone ID suffix (e.g. %%eth0 or %%1)?"); + } + goto bail; + } + + len = sizeof(*src); + rc = getsockname(fd, (struct sockaddr *) src, &len); + if (rc == -1) + netutil_fatal("%s: can't getsockname: %s", __func__, socket_strerror(socket_errno())); + + close(fd); + return 0; + +bail: + close(fd); + return -1; +} + +static char *lookup_ifindex(unsigned int index, int af, char *namebuf, size_t len) { + intf_t *it; + struct intf_entry entry; + int rc; + + it = intf_open(); + assert(it != NULL); + entry.intf_len = sizeof(entry); + rc = intf_get_index(it, &entry, af, index); + intf_close(it); + if (rc == -1) + return NULL; + + Strncpy(namebuf, entry.intf_name, len); + return namebuf; +} + +static int route_dst_generic(const struct sockaddr_storage *dst, + struct route_nfo *rnfo, const char *device, + const struct sockaddr_storage *spoofss) { + struct interface_info *ifaces; + struct interface_info *iface; + int numifaces = 0; + struct sys_route *routes; + int numroutes = 0; + int i; + char namebuf[32]; + char errstr[256]; + errstr[0]='\0'; + + if (!dst) + netutil_fatal("%s passed a NULL dst address", __func__); + + if(spoofss!=NULL){ + /* Throughout the rest of this function we only change rnfo->srcaddr if the source isn't spoofed */ + memcpy(&rnfo->srcaddr, spoofss, sizeof(rnfo->srcaddr)); + /* The device corresponding to this spoofed address should already have been set elsewhere. */ + assert(device!=NULL && device[0]!='\0'); + } + + if (device == NULL || device[0] == '\0') { + /* Check if there is an interface scope on the address which we must use. */ + if (dst->ss_family == AF_INET6) { + const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) dst; + if (sin6->sin6_scope_id > 0) { + device = lookup_ifindex(sin6->sin6_scope_id, sin6->sin6_family, namebuf, sizeof(namebuf)); + if (device == NULL) { + netutil_error("Could not find interface with index %u", (unsigned int) sin6->sin6_scope_id); + return 0; + } + } + } + } + + if (device!=NULL && device[0]!='\0'){ + iface = getInterfaceByName(device, dst->ss_family); + if (!iface) + netutil_fatal("Could not find interface %s", device); + } else { + iface = NULL; + } + + if((routes=getsysroutes(&numroutes, errstr, sizeof(errstr)))==NULL) + netutil_fatal("%s: Failed to obtain system routes: %s", __func__, errstr); + if((ifaces=getinterfaces(&numifaces, errstr, sizeof(errstr)))==NULL) + netutil_fatal("%s: Failed to obtain system interfaces: %s", __func__, errstr); + + /* First check if dst is one of the localhost's own addresses. We need to use + a localhost device for these. */ + for (i = 0; i < numifaces; i++) { + struct interface_info *loopback; + + if (!sockaddr_equal(dst, &ifaces[i].addr)) + continue; + + if (ifaces[i].device_type == devt_loopback) + loopback = &ifaces[i]; + else + loopback = find_loopback_iface(ifaces, numifaces); + if (loopback == NULL) + /* Hmmm ... no localhost -- move on to the routing table. */ + break; + + if (iface != NULL && strcmp(loopback->devname, iface->devname) != 0) + continue; + + if (iface == NULL && !loopback->device_up) + continue; + + rnfo->ii = *loopback; + rnfo->direct_connect = 1; + /* But the source address we want to use is the target address. */ + if (!spoofss) { + if (get_srcaddr(dst, &rnfo->srcaddr) == -1) + rnfo->srcaddr = rnfo->ii.addr; + } + + return 1; + } + + /* Go through the routing table and take the first match. getsysroutes sorts + so more-specific routes come first. */ + for (i = 0; i < numroutes; i++) { + if (!sockaddr_equal_netmask(dst, &routes[i].dest, routes[i].netmask_bits)) + continue; + /* Ignore routes that aren't on the device we specified. */ + if (iface != NULL && strcmp(routes[i].device->devname, iface->devname) != 0) + continue; + + if (iface == NULL && !routes[i].device->device_up) + continue; + + rnfo->ii = *routes[i].device; + /* At this point we don't whether this route is direct or indirect ("G" flag + in netstat). We guess that a route is direct when the gateway address is + 0.0.0.0 or ::, when it exactly matches the interface address, or when it + exactly matches the destination address. */ + rnfo->direct_connect = (sockaddr_equal_zero(&routes[i].gw) || + sockaddr_equal(&routes[i].gw, &routes[i].device->addr) || + sockaddr_equal(&routes[i].gw, dst)); + if (!spoofss) { + if (get_srcaddr(dst, &rnfo->srcaddr) == -1) + rnfo->srcaddr = rnfo->ii.addr; + } + rnfo->nexthop = routes[i].gw; + + return 1; + } + + /* No match on routes. Try interfaces directly. */ + for (i = 0; i < numifaces; i++) { + if (!sockaddr_equal_netmask(dst, &ifaces[i].addr, ifaces[i].netmask_bits)) + continue; + if (iface != NULL && strcmp(ifaces[i].devname, iface->devname) != 0) + continue; + + if (iface == NULL && !ifaces[i].device_up) + continue; + + rnfo->ii = ifaces[i]; + rnfo->direct_connect = 1; + if (!spoofss) { + if (get_srcaddr(dst, &rnfo->srcaddr) == -1) + rnfo->srcaddr = rnfo->ii.addr; + } + + return 1; + } + + return 0; +} +#endif + +/* Takes a destination address (dst) and tries to determine the + * source address and interface necessary to route to this address. + * If no route is found, 0 is returned and "rnfo" is undefined. If + * a route is found, 1 is returned and "rnfo" is filled in with all + * of the routing details. If the source address needs to be spoofed, + * it should be passed through "spoofss" (otherwise NULL should be + * specified), along with a suitable network device (parameter "device"). + * Even if spoofss is NULL, if user specified a network device with -e, + * it should still be passed. Note that it's OK to pass either NULL or + * an empty string as the "device", as long as spoofss==NULL. */ +int route_dst(const struct sockaddr_storage *dst, struct route_nfo *rnfo, + const char *device, const struct sockaddr_storage *spoofss) { +#ifdef HAVE_LINUX_RTNETLINK_H + return route_dst_netlink(dst, rnfo, device, spoofss); +#else + return route_dst_generic(dst, rnfo, device, spoofss); +#endif +} + +/* Wrapper for system function sendto(), which retries a few times when + * the call fails. It also prints informational messages about the + * errors encountered. It returns the number of bytes sent or -1 in + * case of error. */ +int Sendto(const char *functionname, int sd, + const unsigned char *packet, int len, unsigned int flags, + struct sockaddr *to, int tolen) { + + int res; + int retries = 0; + int sleeptime = 0; + static int numerrors = 0; + + do { + if ((res = sendto(sd, (const char *) packet, len, flags, to, tolen)) == -1) { + int err = socket_errno(); + + numerrors++; + if(numerrors <= 10) { + netutil_error("sendto in %s: sendto(%d, packet, %d, 0, %s, %d) => %s", + functionname, sd, len, inet_ntop_ez((struct sockaddr_storage *) to, sizeof(struct sockaddr_storage)), tolen, + strerror(err)); + netutil_error("Offending packet: %s", ippackethdrinfo(packet, len, LOW_DETAIL)); + if (numerrors == 10) { + netutil_error("Omitting future %s error messages now that %d have been shown. Use -d2 if you really want to see them.", __func__, numerrors); + } + } +#if WIN32 + return -1; +#else + if (retries > 2) + return -1; + /* For these enumerated errors, we sleep and try again. */ + if (!(err == ENOBUFS || err == ENOMEM)) + return -1; + sleeptime = 15 * (1 << (2 * retries)); + netutil_error("Sleeping %d seconds then retrying", sleeptime); + fflush(stderr); + sleep(sleeptime); +#endif + } + retries++; + } while (res == -1); + + return res; +} + + + +/* Send an IP packet over an ethernet handle. */ +int send_ip_packet_eth(const struct eth_nfo *eth, const u8 *packet, unsigned int packetlen) { + eth_t *ethsd; + u8 *eth_frame; + int res; + + eth_frame = (u8 *) safe_malloc(14 + packetlen); + memcpy(eth_frame + 14, packet, packetlen); + eth_pack_hdr(eth_frame, eth->dstmac, eth->srcmac, ETH_TYPE_IP); + if (!eth->ethsd) { + ethsd = eth_open_cached(eth->devname); + if (!ethsd) + netutil_fatal("%s: Failed to open ethernet device (%s)", __func__, eth->devname); + } else { + ethsd = eth->ethsd; + } + res = eth_send(ethsd, eth_frame, 14 + packetlen); + /* No need to close ethsd due to caching */ + free(eth_frame); + + return res; +} + + +/* Send an IP packet over a raw socket. */ +int send_ip_packet_sd(int sd, const struct sockaddr_in *dst, + const u8 *packet, unsigned int packetlen) { + struct sockaddr_in sock; + struct ip *ip = (struct ip *) packet; + struct tcp_hdr *tcp; + struct udp_hdr *udp; + int res; + + assert(sd >= 0); + sock = *dst; + + /* It is bogus that I need the address and port info when sending a RAW IP + packet, but it doesn't seem to work w/o them */ + if (packetlen >= 20) { + if (ip->ip_p == IPPROTO_TCP + && packetlen >= (unsigned int) ip->ip_hl * 4 + 20) { + tcp = (struct tcp_hdr *) ((u8 *) ip + ip->ip_hl * 4); + sock.sin_port = tcp->th_dport; + } else if (ip->ip_p == IPPROTO_UDP + && packetlen >= (unsigned int) ip->ip_hl * 4 + 8) { + udp = (struct udp_hdr *) ((u8 *) ip + ip->ip_hl * 4); + sock.sin_port = udp->uh_dport; + } + } + + /* Equally bogus is that the IP total len and IP fragment offset + fields need to be in host byte order on certain BSD variants. I + must deal with it here rather than when building the packet, + because they should be in NBO when I'm sending over raw + ethernet */ +#if (defined(FREEBSD) && (__FreeBSD_version < 1100030)) || BSDI || NETBSD || DEC || MACOSX + ip->ip_len = ntohs(ip->ip_len); + ip->ip_off = ntohs(ip->ip_off); +#endif + + res = Sendto("send_ip_packet_sd", sd, packet, packetlen, 0, + (struct sockaddr *) &sock, + (int) sizeof(struct sockaddr_in)); + + /* Undo the byte order switching. */ +#if (defined(FREEBSD) && (__FreeBSD_version < 1100030)) || BSDI || NETBSD || DEC || MACOSX + ip->ip_len = htons(ip->ip_len); + ip->ip_off = htons(ip->ip_off); +#endif + + return res; +} + + + +/* Sends the supplied pre-built IPv4 packet. The packet is sent through + * the raw socket "sd" if "eth" is NULL. Otherwise, it gets sent at raw + * ethernet level. */ +int send_ip_packet_eth_or_sd(int sd, const struct eth_nfo *eth, + const struct sockaddr_in *dst, + const u8 *packet, unsigned int packetlen) { + if(eth) + return send_ip_packet_eth(eth, packet, packetlen); + else + return send_ip_packet_sd(sd, dst, packet, packetlen); +} + + + +/* Create and send all fragments of a pre-built IPv4 packet + * Minimal MTU for IPv4 is 68 and maximal IPv4 header size is 60 + * which gives us a right to cut TCP header after 8th byte + * (shouldn't we inflate the header to 60 bytes too?) */ +int send_frag_ip_packet(int sd, const struct eth_nfo *eth, + const struct sockaddr_in *dst, + const u8 *packet, unsigned int packetlen, u32 mtu) { + struct ip *ip = (struct ip *) packet; + int headerlen = ip->ip_hl * 4; // better than sizeof(struct ip) + u32 datalen = packetlen - headerlen; + int fdatalen = 0, res = 0; + int fragment=0; + + assert(headerlen <= (int) packetlen); + assert(headerlen >= 20 && headerlen <= 60); // sanity check (RFC791) + assert(mtu > 0 && mtu % 8 == 0); // otherwise, we couldn't set Fragment offset (ip->ip_off) correctly + + if (datalen <= mtu) { + netutil_error("Warning: fragmentation (mtu=%lu) requested but the payload is too small already (%lu)", (unsigned long)mtu, (unsigned long)datalen); + return send_ip_packet_eth_or_sd(sd, eth, dst, packet, packetlen); + } + + u8 *fpacket = (u8 *) safe_malloc(headerlen + mtu); + memcpy(fpacket, packet, headerlen + mtu); + ip = (struct ip *) fpacket; + + // create fragments and send them + for (fragment = 1; fragment * mtu < datalen + mtu; fragment++) { + fdatalen = (fragment * mtu <= datalen ? mtu : datalen % mtu); + ip->ip_len = htons(headerlen + fdatalen); + ip->ip_off = htons((fragment - 1) * mtu / 8); + if ((fragment - 1) * mtu + fdatalen < datalen) + ip->ip_off |= htons(IP_MF); +#if HAVE_IP_IP_SUM + ip->ip_sum = 0; + ip->ip_sum = in_cksum((unsigned short *) ip, headerlen); +#endif + if (fragment > 1) // copy data payload + memcpy(fpacket + headerlen, + packet + headerlen + (fragment - 1) * mtu, fdatalen); + res = send_ip_packet_eth_or_sd(sd, eth, dst, fpacket, ntohs(ip->ip_len)); + if (res == -1) + break; + } + free(fpacket); + return res; +} + +/* There are three ways to send a raw IPv6 packet. + + send_ipv6_eth works when the device is Ethernet. (Unfortunately IPv6-in-IPv4 + tunnels are not.) We can control all header fields and extension headers. + + send_ipv6_ipproto_raw must be used when IPPROTO_RAW sockets include the IP + header, like IP_HDRINCL for IPv4. This is non-standard but is the case on + Linux. (On other platforms, IPPROTO_RAW has no special meaning and just + stands for protocol 255.) We can control all header fields and extension + headers. This method uses only one raw socket for all sends. + + send_ipv6_ip must be used when IPPROTO_RAW sockets do not include the IP + header. Through standard function calls we can control all header fields + except for the flow label. This method needs one raw socket for every + protocol. (More precisely, one socket per distinct Next Header value.) +*/ + +/* Send an IPv6 packet over an Ethernet handle. */ +static int send_ipv6_eth(const struct eth_nfo *eth, const u8 *packet, unsigned int packetlen) { + eth_t *ethsd; + struct eth_hdr *eth_frame; + u8 *copy; + int res; + + copy = (u8 *) safe_malloc(packetlen + sizeof(*eth_frame)); + memcpy(copy + sizeof(*eth_frame), packet, packetlen); + eth_frame = (struct eth_hdr *) copy; + eth_pack_hdr(eth_frame, eth->dstmac, eth->srcmac, ETH_TYPE_IPV6); + if (!eth->ethsd) { + ethsd = eth_open_cached(eth->devname); + if (!ethsd) + netutil_fatal("%s: Failed to open ethernet device (%s)", __func__, eth->devname); + } else { + ethsd = eth->ethsd; + } + res = eth_send(ethsd, eth_frame, sizeof(*eth_frame) + packetlen); + /* No need to close ethsd due to caching */ + free(eth_frame); + + return res; +} + +#if HAVE_IPV6_IPPROTO_RAW + +/* Send an IPv6 packet over a raw socket, on platforms where IPPROTO_RAW implies + IP_HDRINCL-like behavior. */ +static int send_ipv6_ipproto_raw(const struct sockaddr_in6 *dst, + const unsigned char *packet, unsigned int packetlen) { + int sd, n; + + sd = -1; + n = -1; + + sd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); + if (sd == -1) { + perror("socket"); + goto bail; + } + + n = Sendto(__func__, sd, packet, packetlen, 0, (struct sockaddr *) dst, sizeof(*dst)); + +bail: + if (sd != -1) + close(sd); + + return n; +} + +#elif !WIN32 + +/* Add an ancillary cmsghdr data block to the list of blocks in a msghdr. + The list is stored in msg->msg_control, which is dynamically allocated + and reallocated as needed. It must be freed after this function returns. + msg->msg_controllen is also modified by this function. Returns -1 in case of + error or 0 otherwise. */ +static int add_ancillary(struct msghdr *msg, int level, int type, + const void *data, size_t len) +{ + struct cmsghdr *cm; + void *p; + + p = realloc(msg->msg_control, msg->msg_controllen + CMSG_SPACE(len)); + if (p == NULL) + return -1; + msg->msg_control = p; + + cm = (struct cmsghdr *) ((char *) msg->msg_control + msg->msg_controllen); + msg->msg_controllen += CMSG_SPACE(len); + + cm->cmsg_len = CMSG_LEN(len); + cm->cmsg_level = level; + cm->cmsg_type = type; + + memcpy(CMSG_DATA(cm), data, len); + + return 0; +} + +static int exthdr_type_to_cmsg_type(uint8_t type) { + switch (type) { + /* These are the only extension headers we can set directly through a + msghdr. */ + case 0: + return IPV6_HOPOPTS; + case 43: + return IPV6_RTHDR; + case 60: + return IPV6_DSTOPTS; + default: + return -1; + } +} + +static const unsigned char *add_exthdr_ancillary(struct msghdr *msg, + const unsigned char *p, size_t len, unsigned char *proto) { + unsigned char nxt; + size_t extlen; + int cmsg_type; + + cmsg_type = exthdr_type_to_cmsg_type(*proto); + if (cmsg_type == -1) + return NULL; + + if (len < 2) + return NULL; + nxt = *p; + extlen = (*(p + 1) + 1) * 8; + if (len < extlen) + return NULL; + if (add_ancillary(msg, IPPROTO_IPV6, cmsg_type, p, extlen) == -1) + return NULL; + + *proto = nxt; + + return p + extlen; +} + +/* Send an IPv6 packet over a raw socket. This function can control all header + fields except the flow label (and the payload length can only be controlled + indirectly through the length of the payload). + + For most extension header types, we initialize the socket with the given + protocol, which causes the Next Header field to match when the packet is set. + This allows stuffing arbitrary data into extension headers. However, for a + few well-known headers (like Destination and Routing options), this fails + with EPROTOTYPE because there are specialized functions to add these headers + using the IPv6 socket API. These do not offer as much control because they + are controlled by the OS, and may be reordered, for example. */ +static int send_ipv6_ip(const struct sockaddr_in6 *dst, + const unsigned char *packet, size_t packetlen) { + struct msghdr msg; + struct iovec iov; + + const unsigned char *end; + struct ip6_hdr *hdr; + unsigned char nxt; +#ifdef IPV6_TCLASS + int tclass; +#endif + int hoplimit; + + int sd; + int n; + + sd = -1; + n = -1; + + /* Set up sendmsg data structure. iov is filled in below. */ + msg.msg_name = (void *) dst; + msg.msg_namelen = sizeof(*dst); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + if (packetlen < sizeof(*hdr)) + return -1; + hdr = (struct ip6_hdr *) packet; + + /* This can also be set with setsockopt(IPPROTO_IPV6, IPV6_TCLASS). */ +#ifdef IPV6_TCLASS + tclass = ntohl(hdr->ip6_flow & IP6_FLOWINFO_MASK) >> 20; + if (add_ancillary(&msg, IPPROTO_IPV6, + IPV6_TCLASS, &tclass, sizeof(tclass)) == -1) { + goto bail; + } +#endif + /* This can also be set with setsockopt(IPPROTO_IPV6, IPV6_UNICAST_HOPS). */ + hoplimit = hdr->ip6_hlim; + if (add_ancillary(&msg, IPPROTO_IPV6, + IPV6_HOPLIMIT, &hoplimit, sizeof(hoplimit)) == -1) { + goto bail; + } + /* The Next Header field is set when the socket is created. The payload + length is set in the call to sendmsg. There's no way to set the flow + label. */ + + /* We must loop until we find a nh value acceptable to the operating system + (one that can be passed as the third parameter to socket). In my tests on + OS X, you get EPROTOTYPE "Protocol wrong type for socket" for + 43 routing + 44 fragment + 50 ESP + 51 AH + 60 DSTOPT + 108 IPcomp + Some of these we are able to handle with ancillary data. When that's + possible, we skip over the header, add the ancillary data, and try again + with the next header. */ + end = packet + packetlen; + packet += sizeof(*hdr); + nxt = hdr->ip6_nxt; + for (;;) { + errno = 0; + sd = socket(AF_INET6, SOCK_RAW, nxt); + if (!(sd == -1 && errno == EPROTOTYPE)) + break; + packet = add_exthdr_ancillary(&msg, packet, end - packet, &nxt); + if (packet == NULL) { + netutil_error("Can't add extension header %u as ancillary data", nxt); + goto bail; + } + } + if (sd == -1) { + perror("socket"); + goto bail; + } + + assert(packet <= end); + iov.iov_base = (unsigned char *) packet; + iov.iov_len = end - packet; + + n = sendmsg(sd, &msg, 0); + if (n == -1) + perror("sendmsg"); + +bail: + free(msg.msg_control); + if (sd != -1) + close(sd); + + return n; +} + +#endif + +/* For now, the sd argument is ignored. */ +int send_ipv6_packet_eth_or_sd(int sd, const struct eth_nfo *eth, + const struct sockaddr_in6 *dst, const u8 *packet, unsigned int packetlen) { + if (eth != NULL) { + return send_ipv6_eth(eth, packet, packetlen); + } else { +#if HAVE_IPV6_IPPROTO_RAW + return send_ipv6_ipproto_raw(dst, packet, packetlen); +#elif !WIN32 + return send_ipv6_ip(dst, packet, packetlen); +#endif + } + + return -1; +} + + + +#ifdef WIN32 +/* Convert a dnet interface name into the long pcap style. This also caches the + data to speed things up. Fills out pcapdev (up to pcapdevlen) and returns + true if it finds anything. Otherwise returns false. This is only necessary + on Windows. */ +int DnetName2PcapName(const char *dnetdev, char *pcapdev, int pcapdevlen) { + static struct NameCorrelationCache { + char dnetd[64]; + char pcapd[128]; + } *NCC = NULL; + static int NCCsz = 0; + static int NCCcapacity = 0; + static struct NameNotFoundCache { + char dnetd[64]; + } *NNFC = NULL; + static int NNFCsz = 0; + static int NNFCcapacity = 0; + int i; + char tmpdev[128]; + + // Init the cache if not done yet + if (!NCC) { + NCCcapacity = 5; + NCC = + (struct NameCorrelationCache *) safe_zalloc(NCCcapacity * + sizeof(*NCC)); + NCCsz = 0; + } + if (!NNFC) { + NNFCcapacity = 5; + NNFC = + (struct NameNotFoundCache *) safe_zalloc(NNFCcapacity * + sizeof(*NNFC)); + NNFCsz = 0; + } + // First check if the name is already in the cache + for (i = 0; i < NCCsz; i++) { + if (strcmp(NCC[i].dnetd, dnetdev) == 0) { + Strncpy(pcapdev, NCC[i].pcapd, pcapdevlen); + return 1; + } + } + // Check if the name is already in the name not found cache + for (i = 0; i < NNFCsz; i++) { + if (strcmp(NNFC[i].dnetd, dnetdev) == 0) { + return 0; + } + } + // OK, so it isn't in the cache. Let's ask dnet for it. + /* Converts a dnet interface name (ifname) to its pcap equivalent, which is stored in + pcapdev (up to a length of pcapdevlen). Returns 1 and fills in pcapdev if successful. */ + if (eth_get_pcap_devname(dnetdev, tmpdev, sizeof(tmpdev)) != 0) { + // We've got it. Let's add it to the not found cache + if (NNFCsz >= NNFCcapacity) { + NNFCcapacity <<= 2; + NNFC = + (struct NameNotFoundCache *) safe_realloc(NNFC, + NNFCcapacity * + sizeof(*NNFC)); + } + Strncpy(NNFC[NNFCsz].dnetd, dnetdev, sizeof(NNFC[0].dnetd)); + NNFCsz++; + return 0; + } + + // We've got it. Let's add it to the cache + if (NCCsz >= NCCcapacity) { + NCCcapacity <<= 2; + NCC = + (struct NameCorrelationCache *) safe_realloc(NCC, + NCCcapacity * + sizeof(*NCC)); + } + Strncpy(NCC[NCCsz].dnetd, dnetdev, sizeof(NCC[0].dnetd)); + Strncpy(NCC[NCCsz].pcapd, tmpdev, sizeof(NCC[0].pcapd)); + NCCsz++; + Strncpy(pcapdev, tmpdev, pcapdevlen); + return 1; +} +#endif + + +/* This function is used to obtain a packet capture handle to look at + * packets on the network. It is actually a wrapper for libpcap's + * pcap_open_live() that takes care of compatibility issues and error + * checking. The function attempts to open the device up to three times. + * If the call does not succeed the third time, NULL is returned. */ +pcap_t *my_pcap_open_live(const char *device, int snaplen, int promisc, int to_ms){ + char err0r[PCAP_ERRBUF_SIZE]; + pcap_t *pt; + char pcapdev[128]; + int failed = 0; + + assert(device != NULL); + +#ifdef WIN32 + /* Nmap normally uses device names obtained through dnet for interfaces, but + Pcap has its own naming system. So the conversion is done here */ + if (!DnetName2PcapName(device, pcapdev, sizeof(pcapdev))) { + /* Oh crap -- couldn't find the corresponding dev apparently. Let's just go + with what we have then ... */ + Strncpy(pcapdev, device, sizeof(pcapdev)); + } +#else + Strncpy(pcapdev, device, sizeof(pcapdev)); +#endif + +#ifdef __amigaos__ + // Amiga doesn't have pcap_create + // TODO: Does Nmap still work on Amiga? + pt = pcap_open_live(pcapdev, snaplen, promisc, to_ms, err0r); + if (!pt) { + netutil_error("pcap_open_live(%s, %d, %d, %d) FAILED. Reported error: %s.", pcapdev, snaplen, promisc, to_ms, err0r); + return NULL; + } +#else + pt = pcap_create(pcapdev, err0r); + if (!pt) { + netutil_error("pcap_create(%s) FAILED: %s.", pcapdev, err0r); + return NULL; + } + +#define MY_PCAP_SET(func, p_t, val) do {\ + failed = func(p_t, val);\ + if (failed) {\ + netutil_error(#func "(%d) FAILED: %d.", val, failed);\ + pcap_close(p_t);\ + return NULL;\ + }\ +} while(0); + + MY_PCAP_SET(pcap_set_snaplen, pt, snaplen); + MY_PCAP_SET(pcap_set_promisc, pt, promisc); + MY_PCAP_SET(pcap_set_timeout, pt, to_ms); +#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE + MY_PCAP_SET(pcap_set_immediate_mode, pt, 1); +#endif + + failed = pcap_activate(pt); + if (failed < 0) { + // PCAP error + netutil_error("pcap_activate(%s) FAILED: %s.", pcapdev, pcap_geterr(pt)); + pcap_close(pt); + return NULL; + } + else if (failed > 0) { + // PCAP warning, report but assume it'll still work + netutil_error("pcap_activate(%s) WARNING: %s.", pcapdev, pcap_geterr(pt)); + } +#endif /* not __amigaos__ */ + +#ifdef WIN32 + /* We want any responses back ASAP */ + /* This is unnecessary with Npcap since libpcap calls PacketSetMinToCopy(0) + * based on immediate mode. Have not determined if it is needed for WinPcap + * or not, but it's not hurting anything. */ + pcap_setmintocopy(pt, 0); + /* Npcap sets kernel buffer size to 1MB, but user buffer size to 256KB. + * Memory is pretty cheap these days, so lets match the kernel buffer size + * for better performance. */ + pcap_setuserbuffer(pt, 1000000); +#endif + + return pt; +} + + +/* Set a pcap filter */ +void set_pcap_filter(const char *device, pcap_t *pd, const char *bpf, ...) { + va_list ap; + int size; + char buf[3072]; + struct bpf_program fcode; + + va_start(ap, bpf); + size = Vsnprintf(buf, sizeof(buf), bpf, ap); + va_end(ap); + if (size >= (int) sizeof(buf)) + netutil_fatal("%s called with too-large filter arg\n", __func__); + + if (pcap_compile(pd, &fcode, buf, 1, PCAP_NETMASK_UNKNOWN) < 0) + netutil_fatal("Error compiling our pcap filter: %s", pcap_geterr(pd)); + if (pcap_setfilter(pd, &fcode) < 0) + netutil_fatal("Failed to set the pcap filter: %s\n", pcap_geterr(pd)); + pcap_freecode(&fcode); +} + + +/* Return the data offset for the given datalink. This function understands the + datalink types DLT_EN10MB and DLT_LINUX_SLL. Returns -1 on error. */ +int datalink_offset(int datalink) +{ + int offset = -1; + /* NOTE: IF A NEW OFFSET EVER EXCEEDS THE CURRENT MAX (24), ADJUST + MAX_LINK_HEADERSZ in libnetutil/netutil.h */ + switch (datalink) { + case DLT_EN10MB: + offset = ETH_HDR_LEN; + break; + case DLT_IEEE802: + offset = 22; + break; +#ifdef __amigaos__ + case DLT_MIAMI: + offset = 16; + break; +#endif +#ifdef DLT_LOOP + case DLT_LOOP: +#endif + case DLT_NULL: + offset = 4; + break; + case DLT_SLIP: +#ifdef DLT_SLIP_BSDOS + case DLT_SLIP_BSDOS: +#endif +#if (FREEBSD || OPENBSD || NETBSD || BSDI || MACOSX) + offset = 16; +#else + offset = 24; /* Anyone use this??? */ +#endif + break; + case DLT_PPP: +#ifdef DLT_PPP_BSDOS + case DLT_PPP_BSDOS: +#endif +#ifdef DLT_PPP_SERIAL + case DLT_PPP_SERIAL: +#endif +#ifdef DLT_PPP_ETHER + case DLT_PPP_ETHER: +#endif +#if (FREEBSD || OPENBSD || NETBSD || BSDI || MACOSX) + offset = 4; +#else +#ifdef SOLARIS + offset = 8; +#else + offset = 24; /* Anyone use this? */ +#endif /* ifdef solaris */ +#endif /* if freebsd || openbsd || netbsd || bsdi */ + break; + case DLT_RAW: + offset = 0; + break; + case DLT_FDDI: + offset = 21; + break; +#ifdef DLT_ENC + case DLT_ENC: + offset = 12; + break; +#endif /* DLT_ENC */ +#ifdef DLT_LINUX_SLL + case DLT_LINUX_SLL: + offset = 16; + break; +#endif +#ifdef DLT_IPNET + case DLT_IPNET: + offset = 24; + break; +#endif + default: + offset = -1; + break; + } + return offset; +} + +/* Common subroutine for reading ARP and NS responses. Input parameters are pd, + to_usec, and accept_callback. If a received frame passes accept_callback, + then the output parameters p, head, rcvdtime, datalink, and offset are filled + in, and the function returns 1. If no frame passes before the timeout, then + the function returns 0 and the output parameters are undefined. */ +int read_reply_pcap(pcap_t *pd, long to_usec, + bool (*accept_callback)(const unsigned char *, const struct pcap_pkthdr *, int, size_t), + const unsigned char **p, struct pcap_pkthdr **head, struct timeval *rcvdtime, + int *datalink, size_t *offset) +{ + static int warning = 0; + int timedout = 0; + int badcounter = 0; + struct timeval tv_start, tv_end; + int ioffset; + + if (!pd) + netutil_fatal("NULL packet device passed to %s", __func__); + + if (to_usec < 0) { + if (!warning) { + warning = 1; + netutil_error("WARNING: Negative timeout value (%lu) passed to %s() -- using 0", to_usec, __func__); + } + to_usec = 0; + } + + /* New packet capture device, need to recompute offset */ + if ((*datalink = pcap_datalink(pd)) < 0) + netutil_fatal("Cannot obtain datalink information: %s", pcap_geterr(pd)); + ioffset = datalink_offset(*datalink); + if (ioffset < 0) + netutil_fatal("datalink_offset failed for type %d (DLT_EN10MB = %d, DLT_LINUX_SLL = %d)", *datalink, DLT_EN10MB, DLT_LINUX_SLL); + *offset = (unsigned int) ioffset; + + if (to_usec > 0) { + gettimeofday(&tv_start, NULL); + } + + do { + + *p = NULL; + int pcap_status = 0; + /* It may be that protecting this with !pcap_selectable_fd_one_to_one is not + necessary, that it is always safe to do a nonblocking read in this way on + all platforms. But I have only tested it on Solaris. */ + if (!pcap_selectable_fd_one_to_one()) { + int rc, nonblock; + + nonblock = pcap_getnonblock(pd, NULL); + assert(nonblock == 0); + rc = pcap_setnonblock(pd, 1, NULL); + assert(rc == 0); + pcap_status = pcap_next_ex(pd, head, p); + rc = pcap_setnonblock(pd, nonblock, NULL); + assert(rc == 0); + } + + if (pcap_status == PCAP_ERROR) { + // TODO: Gracefully end the scan. + netutil_fatal("Error from pcap_next_ex: %s\n", pcap_geterr(pd)); + } + + if (pcap_status == 0 || *p == NULL) { + /* Nonblocking pcap_next_ex didn't get anything. */ + if (pcap_select(pd, to_usec) == 0) + timedout = 1; + else + pcap_status = pcap_next_ex(pd, head, p); + } + + if (pcap_status == PCAP_ERROR) { + // TODO: Gracefully end the scan. + netutil_fatal("Error from pcap_next_ex: %s\n", pcap_geterr(pd)); + } + + if (pcap_status == 1 && *p != NULL && accept_callback(*p, *head, *datalink, *offset)) { + break; + } else if (pcap_status == 0 || *p == NULL) { + /* Should we timeout? */ + if (to_usec == 0) { + timedout = 1; + } else if (to_usec > 0) { + gettimeofday(&tv_end, NULL); + if (TIMEVAL_SUBTRACT(tv_end, tv_start) >= to_usec) { + timedout = 1; + } + } + } else { + /* We'll be a bit patient if we're getting actual packets back, but + not indefinitely so */ + if (badcounter++ > 50) + timedout = 1; + } + } while (!timedout); + + if (timedout) + return 0; + + if (rcvdtime) { + // TODO: come up with a better way to synchronize pcap with gettimeofday. + // Npcap and WinPcap both suffer from clock drift relative to gettimeofday(). + // We hope to fix this with better time sources for Npcap ( http://issues.nmap.org/1407 ) + // and for Nmap ( http://issues.nmap.org/180 ) + // For now, we use gettimeofday() for Windows in this case. + // Sometimes the time from the pcap header is a + // COUPLE SECONDS before the gettimeofday() results :(. +#if defined(WIN32) || defined(__amigaos__) + gettimeofday(&tv_end, NULL); + *rcvdtime = tv_end; +#else + rcvdtime->tv_sec = (*head)->ts.tv_sec; + rcvdtime->tv_usec = (*head)->ts.tv_usec; + assert((*head)->ts.tv_sec); +#endif + } + + return 1; +} + +static bool accept_arp(const unsigned char *p, const struct pcap_pkthdr *head, + int datalink, size_t offset) +{ + if (head->caplen < offset + 28) + return false; + + /* hw type eth (0x0001), prot ip (0x0800), + hw size (0x06), prot size (0x04) */ + if (memcmp(p + offset, "\x00\x01\x08\x00\x06\x04\x00\x02", 8) != 0) + return false; + + if (datalink == DLT_EN10MB) { + return ntohs(*((u16 *) (p + 12))) == ETH_TYPE_ARP; + } else if (datalink == DLT_LINUX_SLL) { + return ntohs(*((u16 *) (p + 2))) == ARPHRD_ETHER && /* sll_hatype */ + ntohs(*((u16 *) (p + 4))) == 6 && /* sll_halen */ + ntohs(*((u16 *) (p + 14))) == ETH_TYPE_ARP; /* sll_protocol */ + } else { + return false; + } +} + +/* Attempts to read one IPv4/Ethernet ARP reply packet from the pcap + descriptor pd. If it receives one, fills in sendermac (must pass + in 6 bytes), senderIP, and rcvdtime (can be NULL if you don't care) + and returns 1. If it times out and reads no arp requests, returns + 0. to_usec is the timeout period in microseconds. Use 0 to avoid + blocking to the extent possible. Returns -1 or exits if there is + an error. The last parameter is a pointer to a callback function + that can be used for packet tracing. This is intended to be used + by Nmap only. Any other calling this should pass NULL instead. */ +int read_arp_reply_pcap(pcap_t *pd, u8 *sendermac, + struct in_addr *senderIP, long to_usec, + struct timeval *rcvdtime, + void (*trace_callback)(int, const u8 *, u32, struct timeval *)) { + const unsigned char *p; + struct pcap_pkthdr *head; + int datalink; + size_t offset; + int rc; + + rc = read_reply_pcap(pd, to_usec, accept_arp, &p, &head, rcvdtime, &datalink, &offset); + if (rc == 0) + return 0; + + memcpy(sendermac, p + offset + 8, 6); + /* I think alignment should allow this ... */ + memcpy(&senderIP->s_addr, p + offset + 14, 4); + + if (trace_callback != NULL) { + /* TODO: First parameter "2" is a hardcoded value for Nmap's PacketTrace::RECV. */ + trace_callback(2, (u8 *) p + offset, ARP_HDR_LEN + ARP_ETHIP_LEN, rcvdtime); + } + + return 1; +} + +static bool accept_ns(const unsigned char *p, const struct pcap_pkthdr *head, + int datalink, size_t offset) +{ + struct icmpv6_hdr *icmp6_header; + + if (head->caplen < offset + IP6_HDR_LEN + ICMPV6_HDR_LEN) + return false; + + icmp6_header = (struct icmpv6_hdr *)(p + offset + IP6_HDR_LEN); + return icmp6_header->icmpv6_type == ICMPV6_NEIGHBOR_ADVERTISEMENT && + icmp6_header->icmpv6_code == 0; +} + +/* Attempts to read one IPv6/Ethernet Neighbor Solicitation reply packet from the pcap + descriptor pd. If it receives one, fills in sendermac (must pass + in 6 bytes), senderIP, and rcvdtime (can be NULL if you don't care) + and returns 1. If it times out and reads no Neighbor Advertisement, returns + 0. to_usec is the timeout period in microseconds. Use 0 to avoid + blocking to the extent possible. Returns -1 or exits if there is + an error. The last parameter is a pointer to a callback function + that can be used for packet tracing. This is intended to be used + by Nmap only. Any other calling this should pass NULL instead. */ +int read_ns_reply_pcap(pcap_t *pd, u8 *sendermac, + struct sockaddr_in6 *senderIP, long to_usec, + struct timeval *rcvdtime, bool *has_mac, + void (*trace_callback)(int, const u8 *, u32, struct timeval *)) { + const unsigned char *p; + struct pcap_pkthdr *head; + int datalink; + size_t offset; + int rc; + struct icmpv6_msg_nd *na; + + rc = read_reply_pcap(pd, to_usec, accept_ns, &p, &head, rcvdtime, &datalink, &offset); + if (rc == 0) + return 0; + + na = (struct icmpv6_msg_nd *)(p + offset + IP6_HDR_LEN + ICMPV6_HDR_LEN); + if (head->caplen >= ((unsigned char *)na - p) + sizeof(struct icmpv6_msg_nd) && + na->icmpv6_option_type == 2 && + na->icmpv6_option_length == 1) { + *has_mac = true; + memcpy(sendermac, &na->icmpv6_mac, 6); + } + else { + *has_mac = false; + } + senderIP->sin6_family = AF_INET6; + memcpy(&senderIP->sin6_addr.s6_addr, &na->icmpv6_target, 16); + + if (trace_callback != NULL) { + /* TODO: First parameter "2" is a hardcoded value for Nmap's PacketTrace::RECV. */ + trace_callback(2, (u8 *) p + offset, IP6_HDR_LEN + ICMPV6_HDR_LEN + 4 + 16 + 8, rcvdtime); + } + + return 1; +} + + +/* Issues an Neighbor Solicitation for the MAC of targetss (which will be placed + in targetmac if obtained) from the source IP (srcip) and source mac + (srcmac) given. "The request is ussued using device dev to the + multicast MAC address. The transmission is attempted up to 3 + times. If none of these elicit a response, false will be returned. + If the mac is determined, true is returned. The last parameter is + a pointer to a callback function that can be used for packet tracing. + This is intended to be used by Nmap only. Any other calling this + should pass NULL instead. */ +bool doND(const char *dev, const u8 *srcmac, + const struct sockaddr_storage *srcip, + const struct sockaddr_storage *targetip, + u8 *targetmac, + void (*traceND_callback)(int, const u8 *, u32 , struct timeval *) + ) { + /* timeouts in microseconds ... the first ones are retransmit times, while + the final one is when we give up */ + int timeouts[] = { 100000, 400000, 800000 }; + int max_sends = 3; + int num_sends = 0; // How many we have sent so far + eth_t *ethsd; + u8 frame[ETH_HDR_LEN + IP6_HDR_LEN + ICMPV6_HDR_LEN + 4 + 16 + 8]; + struct timeval start, now, rcvdtime; + int timeleft; + int listenrounds; + int rc; + bool has_mac; + pcap_t *pd; + struct sockaddr_storage rcvdIP; + rcvdIP.ss_family = AF_INET6; + bool foundit = false; + char filterstr[256]; + struct sockaddr_in6 *target_sin6, *src_sin6; + struct sockaddr_in6 ns_dst_ip6; + + if (targetip->ss_family != AF_INET6 || srcip->ss_family != AF_INET6) + netutil_fatal("%s can only handle IPv6 addresses", __func__); + + target_sin6 = (struct sockaddr_in6 *) targetip; + src_sin6 = (struct sockaddr_in6 *) srcip; + + unsigned char ns_dst_mac[6] = {0x33, 0x33, 0xff}; + ns_dst_mac[3] = target_sin6->sin6_addr.s6_addr[13]; + ns_dst_mac[4] = target_sin6->sin6_addr.s6_addr[14]; + ns_dst_mac[5] = target_sin6->sin6_addr.s6_addr[15]; + + ns_dst_ip6 = *target_sin6; + unsigned char multicast_prefix[13] = {0}; + multicast_prefix[0] = 0xff; + multicast_prefix[1] = 0x02; + multicast_prefix[11] = 0x1; + multicast_prefix[12] = 0xff; + memcpy(ns_dst_ip6.sin6_addr.s6_addr, multicast_prefix, sizeof(multicast_prefix)); + + /* Start listening */ + if((pd=my_pcap_open_live(dev, 100, 1, 25))==NULL) + netutil_fatal("my_pcap_open_live(%s, 50, 1, 25) failed three times.", dev); + /* Libpcap: IPv6 upper-layer protocol is not supported by proto[x] */ + /* Grab the ICMPv6 type using ip6[X:Y] syntax. This works only if there are no + extension headers (top-level nh is IPPROTO_ICMPV6). */ + Snprintf(filterstr, 256, "ether dst %02X%02X%02X%02X%02X%02X and icmp6 and ip6[6:1] = %u and ip6[40:1] = %u", + srcmac[0], srcmac[1], srcmac[2], srcmac[3], srcmac[4], srcmac[5], + IPPROTO_ICMPV6, ICMPV6_NEIGHBOR_ADVERTISEMENT); + set_pcap_filter(dev, pd, filterstr); + + /* Prepare probe and sending stuff */ + ethsd = eth_open_cached(dev); + if (!ethsd) + netutil_fatal("%s: failed to open device %s", __func__, dev); + eth_pack_hdr(frame, *ns_dst_mac, *srcmac, ETH_TYPE_IPV6); + ip6_pack_hdr(frame + ETH_HDR_LEN, 0, 0, 32, 0x3a, 255, *src_sin6->sin6_addr.s6_addr, *ns_dst_ip6.sin6_addr.s6_addr); + icmpv6_pack_hdr_ns_mac(frame + ETH_HDR_LEN + IP6_HDR_LEN, target_sin6->sin6_addr.s6_addr, *srcmac); + ip6_checksum(frame + ETH_HDR_LEN, IP6_HDR_LEN + ICMPV6_HDR_LEN + 4 + 16 + 8); + + gettimeofday(&start, NULL); + gettimeofday(&now, NULL); + + while (!foundit && num_sends < max_sends) { + /* Send the sucker */ + rc = eth_send(ethsd, frame, sizeof(frame)); + if (rc != sizeof(frame)) { + netutil_error("WARNING: %s: eth_send of Neighbor Solicitation packet returned %u rather than expected %d bytes", __func__, rc, (int) sizeof(frame)); + } + if(traceND_callback!=NULL){ + /* TODO: First parameter "1" is a hardcoded value for Nmap's PacketTrace::SENT*/ + traceND_callback(1, (u8 *) frame + ETH_HDR_LEN, IP6_HDR_LEN + ICMPV6_HDR_LEN + 4 + 16 + 8, &now); + } + num_sends++; + + listenrounds = 0; + while (!foundit) { + gettimeofday(&now, NULL); + timeleft = timeouts[num_sends - 1] - TIMEVAL_SUBTRACT(now, start); + if (timeleft < 0) { + if (listenrounds > 0) + break; + else + timeleft = 25000; + } + listenrounds++; + /* Now listen until we reach our next timeout or get an answer */ + rc = read_ns_reply_pcap(pd, targetmac, (struct sockaddr_in6 *) &rcvdIP, timeleft, + &rcvdtime, &has_mac, traceND_callback); + if (rc == -1) + netutil_fatal("%s: Received -1 response from read_ns_reply_pcap", __func__); + if (rc == 1) { + /* Yay, I got one! But is it the right one? */ + if (sockaddr_storage_cmp(&rcvdIP,targetip) != 0) + continue; /* D'oh! */ + foundit = true; /* WOOHOO! */ + } + } + } + + /* OK - let's close up shop ... */ + pcap_close(pd); + /* No need to close ethsd due to caching */ + return foundit; +} + +/* Issues an ARP request for the MAC of targetss (which will be placed + in targetmac if obtained) from the source IP (srcip) and source mac + (srcmac) given. "The request is ussued using device dev to the + broadcast MAC address. The transmission is attempted up to 3 + times. If none of these elicit a response, false will be returned. + If the mac is determined, true is returned. The last parameter is + a pointer to a callback function that can be used for packet tracing. + This is intended to be used by Nmap only. Any other calling this + should pass NULL instead. */ +bool doArp(const char *dev, const u8 *srcmac, + const struct sockaddr_storage *srcip, + const struct sockaddr_storage *targetip, + u8 *targetmac, + void (*traceArp_callback)(int, const u8 *, u32 , struct timeval *) + ) { + /* timeouts in microseconds ... the first ones are retransmit times, while + the final one is when we give up */ + int timeouts[] = { 100000, 400000, 800000 }; + int max_sends = 3; + int num_sends = 0; // How many we have sent so far + eth_t *ethsd; + u8 frame[ETH_HDR_LEN + ARP_HDR_LEN + ARP_ETHIP_LEN]; + const struct sockaddr_in *targetsin = (struct sockaddr_in *) targetip; + const struct sockaddr_in *srcsin = (struct sockaddr_in *) srcip; + struct timeval start, now, rcvdtime; + int timeleft; + int listenrounds; + int rc; + pcap_t *pd; + struct in_addr rcvdIP; + bool foundit = false; + char filterstr[256]; + + if (targetsin->sin_family != AF_INET || srcsin->sin_family != AF_INET) + netutil_fatal("%s can only handle IPv4 addresses", __func__); + + /* Start listening */ + if((pd=my_pcap_open_live(dev, 50, 1, 25))==NULL) + netutil_fatal("my_pcap_open_live(%s, 50, 1, 25) failed three times.", dev); + Snprintf(filterstr, 256, "arp and arp[18:4] = 0x%02X%02X%02X%02X and arp[22:2] = 0x%02X%02X", + srcmac[0], srcmac[1], srcmac[2], srcmac[3], srcmac[4], srcmac[5]); + set_pcap_filter(dev, pd, filterstr); + + /* Prepare probe and sending stuff */ + ethsd = eth_open_cached(dev); + if (!ethsd) + netutil_fatal("%s: failed to open device %s", __func__, dev); + eth_pack_hdr(frame, ETH_ADDR_BROADCAST, *srcmac, ETH_TYPE_ARP); + arp_pack_hdr_ethip(frame + ETH_HDR_LEN, ARP_OP_REQUEST, *srcmac, + srcsin->sin_addr, ETH_ADDR_BROADCAST, + targetsin->sin_addr); + gettimeofday(&start, NULL); + gettimeofday(&now, NULL); + + while (!foundit && num_sends < max_sends) { + /* Send the sucker */ + rc = eth_send(ethsd, frame, sizeof(frame)); + if (rc != sizeof(frame)) { + netutil_error("WARNING: %s: eth_send of ARP packet returned %u rather than expected %d bytes", __func__, rc, (int) sizeof(frame)); + } + if(traceArp_callback!=NULL){ + /* TODO: First parameter "1" is a hardcoded value for Nmap's PacketTrace::SENT*/ + traceArp_callback(1, (u8 *) frame + ETH_HDR_LEN, ARP_HDR_LEN + ARP_ETHIP_LEN, &now); + } + num_sends++; + + listenrounds = 0; + while (!foundit) { + gettimeofday(&now, NULL); + timeleft = timeouts[num_sends - 1] - TIMEVAL_SUBTRACT(now, start); + if (timeleft < 0) { + if (listenrounds > 0) + break; + else + timeleft = 25000; + } + listenrounds++; + /* Now listen until we reach our next timeout or get an answer */ + rc = read_arp_reply_pcap(pd, targetmac, &rcvdIP, timeleft, + &rcvdtime, traceArp_callback); + if (rc == -1) + netutil_fatal("%s: Received -1 response from readarp_reply_pcap", __func__); + if (rc == 1) { + /* Yay, I got one! But is it the right one? */ + if (rcvdIP.s_addr != targetsin->sin_addr.s_addr) + continue; /* D'oh! */ + foundit = true; /* WOOHOO! */ + } + } + } + + /* OK - let's close up shop ... */ + pcap_close(pd); + /* No need to close ethsd due to caching */ + return foundit; +} + + + +static inline bool is_host_separator(int c) { + return c == ' ' || c == '\r' || c == '\n' || c == '\t' || c == '\0'; +} + +/* Read a single host specification from a file, as for -iL and --excludefile. + It returns the length of the string read; an overflow is indicated when the + return value is >= n. Returns 0 if there was no specification to be read. The + buffer is always null-terminated. */ +size_t read_host_from_file(FILE *fp, char *buf, size_t n) +{ + int ch; + size_t i; + + i = 0; + ch = getc(fp); + while (is_host_separator(ch) || ch == '#') { + if (ch == '#') { + /* Skip comments to the end of the line. */ + while ((ch = getc(fp)) != EOF && ch != '\n') + ; + } else { + ch = getc(fp); + } + } + while (ch != EOF && !(is_host_separator(ch) || ch == '#')) { + if (i < n) + buf[i] = ch; + i++; + ch = getc(fp); + } + if (ch != EOF) + ungetc(ch, fp); + if (i < n) + buf[i] = '\0'; + else if (n > 0) + /* Null-terminate even though it was too long. */ + buf[n - 1] = '\0'; + + return i; +} + + +/* Return next target host specification from the supplied stream. + * if parameter "random" is set to true, then the function will + * return a random, non-reserved, IP address in decimal-dot notation */ +const char *grab_next_host_spec(FILE *inputfd, bool random, int argc, const char **argv) { + static char host_spec[1024]; + struct in_addr ip; + size_t n; + + if (random) { + do { + ip.s_addr = get_random_unique_u32(); + } while (ip_is_reserved(&ip)); + Strncpy(host_spec, inet_ntoa(ip), sizeof(host_spec)); + } else if (!inputfd) { + return( (optind < argc)? argv[optind++] : NULL); + } else { + n = read_host_from_file(inputfd, host_spec, sizeof(host_spec)); + if (n == 0) + return NULL; + else if (n >= sizeof(host_spec)) + netutil_fatal("One of the host specifications from your input file is too long (>= %u chars)", (unsigned int) sizeof(host_spec)); + } + return host_spec; +} + + + +/** Tries to increase the open file descriptor limit for this process. + * @param "desired" is the number of desired max open descriptors. Pass a + * negative value to set the maximum allowed. + * @return the number of max open descriptors that could be set, or 0 in case + * of failure. + * @warning if "desired" is less than the current limit, no action is + * performed. This function may only be used to increase the limit, not to + * decrease it. */ +int set_max_open_descriptors(int desired_max) { + #ifndef WIN32 + struct rlimit r; + int maxfds=-1; + int flag=0; + + #if (defined(RLIMIT_OFILE) || defined(RLIMIT_NOFILE)) + + #ifdef RLIMIT_NOFILE + flag=RLIMIT_NOFILE; /* Linux */ + #else + flag=RLIMIT_OFILE; /* BSD */ + #endif + + if (!getrlimit(flag, &r)) { + /* If current limit is less than the desired, try to increase it */ + if(r.rlim_cur < (rlim_t)desired_max){ + if(desired_max<0){ + r.rlim_cur=r.rlim_max; /* Set maximum */ + }else{ + r.rlim_cur = MIN( (int)r.rlim_max, desired_max ); + } + if (setrlimit(flag, &r)) + ; // netutil_debug("setrlimit(%d, %p) failed", flag, r); + if (!getrlimit(flag, &r)) { + maxfds = r.rlim_cur; + return maxfds; + }else { + return 0; + } + } + } + + #endif /* (defined(RLIMIT_OFILE) || defined(RLIMIT_NOFILE)) */ + #endif /* !WIN32 */ + return 0; +} + + +/** Returns the open file descriptor limit for this process. + * @return the number of max open descriptors or 0 in case of failure. */ +int get_max_open_descriptors() { + #ifndef WIN32 + struct rlimit r; + int flag=0; + + #if (defined(RLIMIT_OFILE) || defined(RLIMIT_NOFILE)) + + #ifdef RLIMIT_NOFILE + flag=RLIMIT_NOFILE; /* Linux */ + #else + flag=RLIMIT_OFILE; /* BSD */ + #endif + + if (!getrlimit(flag, &r)) { + return (int)r.rlim_cur; + } + + #endif /* (defined(RLIMIT_OFILE) || defined(RLIMIT_NOFILE)) */ + #endif /* !WIN32 */ + return 0; +} + + +/* Maximize the open file descriptor limit for this process go up to the + max allowed */ +int max_sd() { + return set_max_open_descriptors(-1); +} diff --git a/libnetutil/netutil.h b/libnetutil/netutil.h new file mode 100644 index 0000000..f88241b --- /dev/null +++ b/libnetutil/netutil.h @@ -0,0 +1,565 @@ +/*************************************************************************** + * netutil.h -- The main include file exposing the external API for * + * libnetutil, a library that provides network-related functions or * + * classes that make it easier to handle things like network interfaces, * + * routing tables, raw packet manipulation, etc. The lib was originally * + * written for use in the Nmap Security Scanner ( https://nmap.org ). * + * * + ***********************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/ + * + ***************************************************************************/ + +/* $Id: netutil.h 18098 2010-06-14 11:50:12Z luis $ */ + +#ifndef _NETUTIL_H_ +#define _NETUTIL_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif +#include <pcap.h> +#ifdef __cplusplus +} +#endif + +#include "dnet.h" +#include <nbase.h> + +/* It is VERY important to never change the value of these two constants. + * Specially, OP_FAILURE should never be positive, as some pieces of code take + * that for granted. */ +enum { OP_FAILURE = -1, OP_SUCCESS = 0 }; + + +/* For systems without SCTP in netinet/in.h, such as MacOS X or Win */ +#ifndef IPPROTO_SCTP +#define IPPROTO_SCTP 132 +#endif + +/* Container used for information common to IPv4 and IPv6 headers, used by + ip_get_data. */ +struct abstract_ip_hdr { + u8 version; /* 4 or 6. */ + struct sockaddr_storage src; + struct sockaddr_storage dst; + u8 proto; /* IPv4 proto or IPv6 next header. */ + u8 ttl; /* IPv4 TTL or IPv6 hop limit. */ + u32 ipid; /* IPv4 IP ID or IPv6 flow label. */ +}; + +#if defined(__GNUC__) +#define NORETURN __attribute__((noreturn)) +#elif defined(_MSC_VER) +#define NORETURN __declspec(noreturn) +#else +#define NORETURN +#endif + +NORETURN void netutil_fatal(const char *str, ...) + __attribute__ ((format (printf, 1, 2))); + +int netutil_error(const char *str, ...) + __attribute__ ((format (printf, 1, 2))); + +/* This function converts zero-terminated 'txt' string to binary 'data'. + It is used to parse user input for ip options. Some examples of possible input + strings and results: + '\x01*2\xA2' -> [0x01,0x01,0xA2] // with 'x' number is parsed in hex + '\01\01\255' -> [0x01,0x01,0xFF] // without 'x' its in decimal + '\x01\x00*2' -> [0x01,0x00,0x00] // '*' is copying char + 'R' -> Record Route with 9 slots + 'S 192.168.0.1 172.16.0.1' -> Strict Route with 2 slots + 'L 192.168.0.1 172.16.0.1' -> Loose Route with 2 slots + 'T' -> Record Timestamp with 9 slots + 'U' -> Record Timestamp and Ip Address with 4 slots + On success, the function returns the length of the final binary + options stored in "data". In case of error, OP_FAILURE is returned + and the "errstr" buffer is filled with an error message + (unless it's NULL). Note that the returned error message does NOT + contain a newline character at the end. */ +int parse_ip_options(const char *txt, u8 *data, int datalen, int* firsthopoff, int* lasthopoff, char *errstr, size_t errstrlen); + +/* Resolves the given hostname or IP address with getaddrinfo, and stores the + first result (if any) in *ss and *sslen. The value of port will be set in the + appropriate place in *ss; set to 0 if you don't care. af may be AF_UNSPEC, in + which case getaddrinfo may return e.g. both IPv4 and IPv6 results; which one + is first depends on the system configuration. Returns 0 on success, or a + getaddrinfo return code (suitable for passing to gai_strerror) on failure. + *ss and *sslen are always defined when this function returns 0. */ +int resolve(const char *hostname, unsigned short port, + struct sockaddr_storage *ss, size_t *sslen, int af); + +/* As resolve, but do not do DNS resolution of hostnames; the first argument + must be the string representation of a numeric IP address. */ +int resolve_numeric(const char *ip, unsigned short port, + struct sockaddr_storage *ss, size_t *sslen, int af); + +/* + * Returns 1 if this is a reserved IP address, where "reserved" means + * either a private address, non-routable address, or even a non-reserved + * but unassigned address which has an extremely high probability of being + * black-holed. + * + * We try to optimize speed when ordering the tests. This optimization + * assumes that all byte values are equally likely in the input. + * + * Warning: This function needs frequent attention because IANA has been + * allocating address blocks many times per year (although it's questionable + * how much longer this trend can be kept up). + * + * Check + * <http://www.iana.org/assignments/ipv4-address-space/ipv4-address-space.txt> + * for the most recent assigments and + * <http://www.cymru.com/Documents/bogon-bn-nonagg.txt> for bogon + * netblocks. + */ +int ip_is_reserved(struct in_addr *ip); + + +/* A couple of trivial functions that maintain a cache of IP to MAC + * Address entries. Function mac_cache_get() looks for the IPv4 address + * in ss and fills in the 'mac' parameter and returns true if it is + * found. Otherwise (not found), the function returns false. + * Function mac_cache_set() adds an entry with the given ip (ss) and + * mac address. An existing entry for the IP ss will be overwritten + * with the new MAC address. mac_cache_set() always returns true. */ +int mac_cache_get(const struct sockaddr_storage *ss, u8 *mac); +int mac_cache_set(const struct sockaddr_storage *ss, u8 *mac); + +const void *ip_get_data(const void *packet, unsigned int *len, + struct abstract_ip_hdr *hdr); +const void *ip_get_data_any(const void *packet, unsigned int *len, + struct abstract_ip_hdr *hdr); +/* Get the upper-layer protocol from an IPv4 packet. */ +const void *ipv4_get_data(const struct ip *ip, unsigned int *len); +/* Get the upper-layer protocol from an IPv6 packet. This skips over known + extension headers. The length of the upper-layer payload is stored in *len. + The protocol is stored in *nxt. Returns NULL in case of error. */ +const void *ipv6_get_data(const struct ip6_hdr *ip6, unsigned int *len, u8 *nxt); +const void *ipv6_get_data_any(const struct ip6_hdr *ip6, unsigned int *len, u8 *nxt); +const void *icmp_get_data(const struct icmp_hdr *icmp, unsigned int *len); +const void *icmpv6_get_data(const struct icmpv6_hdr *icmpv6, unsigned int *len); + +/* Standard BSD internet checksum routine. */ +unsigned short in_cksum(u16 *ptr, int nbytes); + +/* Calculate the Internet checksum of some given data concatentated with the + IPv4 pseudo-header. See RFC 1071 and TCP/IP Illustrated sections 3.2, 11.3, + and 17.3. */ +unsigned short ipv4_pseudoheader_cksum(const struct in_addr *src, + const struct in_addr *dst, u8 proto, u16 len, const void *hstart); + +/* Calculate the Internet checksum of some given data concatenated with the + IPv6 pseudo-header. See RFC 2460 section 8.1. */ +u16 ipv6_pseudoheader_cksum(const struct in6_addr *src, + const struct in6_addr *dst, u8 nxt, u32 len, const void *hstart); + +void sethdrinclude(int sd); +void set_ipoptions(int sd, void *opts, size_t optslen); +void set_ttl(int sd, int ttl); + +/* Returns whether the system supports pcap_get_selectable_fd() properly */ +int pcap_selectable_fd_valid(); +int pcap_selectable_fd_one_to_one(); + +/* Call this instead of pcap_get_selectable_fd directly (or your code + won't compile on Windows). On systems which don't seem to support + the pcap_get_selectable_fd() function properly, returns -1, + otherwise simply calls pcap_selectable_fd and returns the + results. If you just want to test whether the function is supported, + use pcap_selectable_fd_valid() instead. */ +int my_pcap_get_selectable_fd(pcap_t *p); + + +/* These two function return -1 if we can't use select() on the pcap + * device, 0 for timeout, and >0 for success. If select() fails we bail + * out because it couldn't work with the file descriptor we got from + * my_pcap_get_selectable_fd() */ +int pcap_select(pcap_t *p, struct timeval *timeout); +int pcap_select(pcap_t *p, long usecs); + +typedef enum { devt_ethernet, devt_loopback, devt_p2p, devt_other } devtype; + +#define MAX_LINK_HEADERSZ 24 +struct link_header { + int datalinktype; /* pcap_datalink(), such as DLT_EN10MB */ + int headerlen; /* 0 if header was too big or unavailaable */ + u8 header[MAX_LINK_HEADERSZ]; +}; + +/* Relevant (to Nmap) information about an interface */ +struct interface_info { + char devname[16]; + char devfullname[16]; /* can include alias info, such as eth0:2. */ + struct sockaddr_storage addr; + u16 netmask_bits; /* CIDR-style. So 24 means class C (255.255.255.0)*/ + devtype device_type; /* devt_ethernet, devt_loopback, devt_p2p, devt_other */ + unsigned int ifindex; /* index (as used by if_indextoname and sin6_scope_id) */ + int device_up; /* True if the device is up (enabled) */ + int mtu; /* Interface's MTU size */ + u8 mac[6]; /* Interface MAC address if device_type is devt_ethernet */ +}; + +struct route_nfo { + struct interface_info ii; + +/* true if the target is directly connected on the network (no routing + required). */ + int direct_connect; + +/* This is the source address that should be used by the packets. It + may be different than ii.addr if you are using localhost interface + to scan the IP of another interface on the machine */ + struct sockaddr_storage srcaddr; + + /* If direct_connect is 0, this is filled in with the next hop + required to route to the target */ + struct sockaddr_storage nexthop; +}; + +struct sys_route { + struct interface_info *device; + struct sockaddr_storage dest; + u16 netmask_bits; + struct sockaddr_storage gw; /* gateway - 0 if none */ + int metric; +}; + +struct eth_nfo { + char srcmac[6]; + char dstmac[6]; + eth_t *ethsd; // Optional, but improves performance. Set to NULL if unavail + char devname[16]; // Only needed if ethsd is NULL. +}; + +/* A simple function that caches the eth_t from dnet for one device, + to avoid opening, closing, and re-opening it thousands of tims. If + you give a different device, this function will close the first + one. Thus this should never be used by programs that need to deal + with multiple devices at once. In addition, you MUST NEVER + eth_close() A DEVICE OBTAINED FROM THIS FUNCTION. Instead, you can + call eth_close_cached() to close whichever device (if any) is + cached. Returns NULL if it fails to open the device. */ +eth_t *eth_open_cached(const char *device); + +/* See the description for eth_open_cached */ +void eth_close_cached(); + +/* Takes a protocol number like IPPROTO_TCP, IPPROTO_UDP, or + * IPPROTO_IP and returns a ascii representation (or "unknown" if it + * doesn't recognize the number). Returned string is in lowercase. */ +const char *proto2ascii_lowercase(u8 proto) ; + +/* Same as proto2ascii() but returns a string in uppercase. */ +const char *proto2ascii_uppercase(u8 proto); + +/* 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 tcppacketoptinfo(u8 *optp, int len, char *result, int bufsize); + +/* Convert an IP address to the device (IE ppp0 eth0) using that + * address. Supplied "dev" must be able to hold at least 32 bytes. + * Returns 0 on success or -1 in case of error. */ +int ipaddr2devname( char *dev, const struct sockaddr_storage *addr ); + +/* Convert a network interface name (IE ppp0 eth0) to an IP address. + * Returns 0 on success or -1 in case of error. */ +int devname2ipaddr(char *dev, struct sockaddr_storage *addr); + +int sockaddr_equal(const struct sockaddr_storage *a, + const struct sockaddr_storage *b); + +int sockaddr_equal_netmask(const struct sockaddr_storage *a, + const struct sockaddr_storage *b, u16 nbits); + +int sockaddr_equal_zero(const struct sockaddr_storage *s); + +/* Returns an allocated array of struct interface_info representing the + available interfaces. The number of interfaces is returned in *howmany. This + function just does caching of results; the real work is done in + getinterfaces_dnet() or getinterfaces_siocgifconf(). + On error, NULL is returned, howmany is set to -1 and the supplied + error buffer "errstr", if not NULL, will contain an error message. */ +struct interface_info *getinterfaces(int *howmany, char *errstr, size_t errstrlen); +/* Frees the array of cached struct interface_info used by getinterfaces. Can + be used to force a refresh or to release memory. */ +void freeinterfaces(void); + +/* This struct is abused to carry either routes or interfaces, depending on the + function it's used in. */ +struct dnet_collector_route_nfo { + struct sys_route *routes; + int numroutes; + int capacity; /* Capacity of routes or ifaces, depending on context */ + struct interface_info *ifaces; + int numifaces; +}; + +/* Looks for an interface with the given name (iname) and address + family type, and returns the corresponding interface_info if found. + Will accept a match of devname or devfullname. Returns NULL if + none found */ +struct interface_info *getInterfaceByName(const char *iname, int af); + +/* Parse the system routing table, converting each route into a + sys_route entry. Returns an array of sys_routes. numroutes is set + to the number of routes in the array. The routing table is only + read the first time this is called -- later results are cached. + The returned route array is sorted by netmask with the most + specific matches first. + On error, NULL is returned, howmany is set to -1 and the supplied + error buffer "errstr", if not NULL, will contain an error message. */ +struct sys_route *getsysroutes(int *howmany, char *errstr, size_t errstrlen); + +/* Tries to determine whether the supplied address corresponds to + * localhost. (eg: the address is something like 127.x.x.x, the address + * matches one of the local network interfaces' address, etc). + * Returns 1 if the address is thought to be localhost and 0 otherwise */ +int islocalhost(const struct sockaddr_storage *ss); + +/* Determines whether the supplied address corresponds to a private, + * non-Internet-routable address. See RFC1918 for details. + * Also checks for link-local addresses per RFC3927. + * Returns 1 if the address is private or 0 otherwise. */ +int isipprivate(const struct sockaddr_storage *addr); + +/* Takes binary data found in the IP Options field of an IPv4 packet + * and returns a string containing an ASCII description of the options + * found. The function returns a pointer to a static buffer that + * subsequent calls will overwrite. On error, NULL is returned. */ +char *format_ip_options(const u8* ipopt, int ipoptlen); + +/* Returns a buffer of ASCII information about an IP packet that may + * look like "TCP 127.0.0.1:50923 > 127.0.0.1:3 S ttl=61 id=39516 + * iplen=40 seq=625950769" or "ICMP PING (0/1) ttl=61 id=39516 iplen=40". + * Returned buffer is static so it is NOT safe to call this in + * multi-threaded environments without appropriate sync protection, or + * call it twice in the same sentence (eg: as two printf parameters). + * Obviously, the caller should never attempt to free() the buffer. The + * returned buffer is guaranteed to be NULL-terminated but no + * assumptions should be made concerning its length. + * + * The function provides full support for IPv4,TCP,UDP,SCTP and ICMPv4. + * It also provides support for standard IPv6 but not for its extension + * headers. If an IPv6 packet contains an ICMPv6 Header, the output will + * reflect this but no parsing of ICMPv6 contents will be performed. + * + * The output has three different levels of detail. Parameter "detail" + * determines how verbose the output should be. It should take one of + * the following values: + * + * LOW_DETAIL (0x01): Traditional output. + * MEDIUM_DETAIL (0x02): More verbose than traditional. + * HIGH_DETAIL (0x03): Contents of virtually every field of the + * protocol headers . + */ +#define LOW_DETAIL 1 +#define MEDIUM_DETAIL 2 +#define HIGH_DETAIL 3 +const char *ippackethdrinfo(const u8 *packet, u32 len, int detail); + + +/* Takes an IPv4 destination address (dst) and tries to determine the + * source address and interface necessary to route to this address. + * If no route is found, 0 is returned and "rnfo" is undefined. If + * a route is found, 1 is returned and "rnfo" is filled in with all + * of the routing details. If the source address needs to be spoofed, + * it should be passed through "spoofss" (otherwise NULL should be + * specified), along with a suitable network device (parameter "device"). + * Even if spoofss is NULL, if user specified a network device with -e, + * it should still be passed. Note that it's OK to pass either NULL or + * an empty string as the "device", as long as spoofss==NULL. */ +int route_dst(const struct sockaddr_storage *dst, struct route_nfo *rnfo, + const char *device, const struct sockaddr_storage *spoofss); + +/* Send an IP packet over a raw socket. */ +int send_ip_packet_sd(int sd, const struct sockaddr_in *dst, const u8 *packet, unsigned int packetlen); + +/* Send an IP packet over an ethernet handle. */ +int send_ip_packet_eth(const struct eth_nfo *eth, const u8 *packet, unsigned int packetlen); + +/* Sends the supplied pre-built IPv4 packet. The packet is sent through + * the raw socket "sd" if "eth" is NULL. Otherwise, it gets sent at raw + * ethernet level. */ +int send_ip_packet_eth_or_sd(int sd, const struct eth_nfo *eth, + const struct sockaddr_in *dst, const u8 *packet, unsigned int packetlen); + +/* Sends an IPv4 packet. */ +int send_ipv6_packet_eth_or_sd(int sd, const struct eth_nfo *eth, + const struct sockaddr_in6 *dst, const u8 *packet, unsigned int packetlen); + +/* Create and send all fragments of a pre-built IPv4 packet. + * Minimal MTU for IPv4 is 68 and maximal IPv4 header size is 60 + * which gives us a right to cut TCP header after 8th byte */ +int send_frag_ip_packet(int sd, const struct eth_nfo *eth, + const struct sockaddr_in *dst, + const u8 *packet, unsigned int packetlen, u32 mtu); + +/* Wrapper for system function sendto(), which retries a few times when + * the call fails. It also prints informational messages about the + * errors encountered. It returns the number of bytes sent or -1 in + * case of error. */ +int Sendto(const char *functionname, int sd, const unsigned char *packet, + int len, unsigned int flags, struct sockaddr *to, int tolen); + +/* This function is used to obtain a packet capture handle to look at + * packets on the network. It is actually a wrapper for libpcap's + * pcap_open_live() that takes care of compatibility issues and error + * checking. Prints an error and fatal()s if the call fails, so a + * valid pcap_t will always be returned. */ +pcap_t *my_pcap_open_live(const char *device, int snaplen, int promisc, int to_ms); + +/* Set a pcap filter */ +void set_pcap_filter(const char *device, pcap_t *pd, const char *bpf, ...); + +/* Issues an ARP request for the MAC of targetss (which will be placed + in targetmac if obtained) from the source IP (srcip) and source mac + (srcmac) given. "The request is ussued using device dev to the + broadcast MAC address. The transmission is attempted up to 3 + times. If none of these elicit a response, false will be returned. + If the mac is determined, true is returned. The last parameter is + a pointer to a callback function that can be used for packet traceing. + This is intended to be used by Nmap only. Any other calling this + should pass NULL instead. */ +bool doArp(const char *dev, const u8 *srcmac, + const struct sockaddr_storage *srcip, + const struct sockaddr_storage *targetip, + u8 *targetmac, + void (*traceArp_callback)(int, const u8 *, u32 , struct timeval *)); + + +/* Issues an Neighbor Solicitation for the MAC of targetss (which will be placed + in targetmac if obtained) from the source IP (srcip) and source mac + (srcmac) given. "The request is ussued using device dev to the + multicast MAC address. The transmission is attempted up to 3 + times. If none of these elicit a response, false will be returned. + If the mac is determined, true is returned. The last parameter is + a pointer to a callback function that can be used for packet tracing. + This is intended to be used by Nmap only. Any other calling this + should pass NULL instead. */ +bool doND(const char *dev, const u8 *srcmac, + const struct sockaddr_storage *srcip, + const struct sockaddr_storage *targetip, + u8 *targetmac, + void (*traceArp_callback)(int, const u8 *, u32 , struct timeval *) + ) ; + +/* Attempts to read one IPv4/Ethernet ARP reply packet from the pcap + descriptor pd. If it receives one, fills in sendermac (must pass + in 6 bytes), senderIP, and rcvdtime (can be NULL if you don't care) + and returns 1. If it times out and reads no arp requests, returns + 0. to_usec is the timeout period in microseconds. Use 0 to avoid + blocking to the extent possible. Returns -1 or exits if there is + an error. The last parameter is a pointer to a callback function + that can be used for packet tracing. This is intended to be used + by Nmap only. Any other calling this should pass NULL instead. */ +int read_arp_reply_pcap(pcap_t *pd, u8 *sendermac, + struct in_addr *senderIP, long to_usec, + struct timeval *rcvdtime, + void (*traceArp_callback)(int, const u8 *, u32 , struct timeval *)); +int read_ns_reply_pcap(pcap_t *pd, u8 *sendermac, + struct sockaddr_in6 *senderIP, long to_usec, + struct timeval *rcvdtime, bool *has_mac, + void (*traceArp_callback)(int, const u8 *, u32 , struct timeval *)); + +/* Attempts to read one IP packet from the pcap descriptor pd. Input parameters are pd, + to_usec, and accept_callback. If a received frame passes accept_callback, + then the output parameters p, head, rcvdtime, datalink, and offset are filled + in, and the function returns 1. If no frame passes before the timeout, then + the function returns 0 and the output parameters are undefined. */ +int read_reply_pcap(pcap_t *pd, long to_usec, + bool (*accept_callback)(const unsigned char *, const struct pcap_pkthdr *, int, size_t), + const unsigned char **p, struct pcap_pkthdr **head, struct timeval *rcvdtime, + int *datalink, size_t *offset); + +/* Read a single host specification from a file, as for -iL and --excludefile. + It returns the length of the string read; an overflow is indicated when the + return value is >= n. Returns 0 if there was no specification to be read. The + buffer is always null-terminated. */ +size_t read_host_from_file(FILE *fp, char *buf, size_t n); + +/* Return next target host specification from the supplied stream. + * if parameter "random" is set to true, then the function will + * return a random, non-reserved, IP address in decimal-dot notation */ +const char *grab_next_host_spec(FILE *inputfd, bool random, int argc, const char **fakeargv); + +#ifdef WIN32 +/* Convert a dnet interface name into the long pcap style. This also caches the + data to speed things up. Fills out pcapdev (up to pcapdevlen) and returns + true if it finds anything. Otherwise returns false. This is only necessary + on Windows. */ +int DnetName2PcapName(const char *dnetdev, char *pcapdev, int pcapdevlen); +#endif + +/** Tries to increase the open file descriptor limit for this process. + * @param "desired" is the number of desired max open descriptors. Pass a + * negative value to set the maximum allowed. + * @return the number of max open descriptors that could be set, or 0 in case + * of failure. + * @warning if "desired" is less than the current limit, no action is + * performed. This function may only be used to increase the limit, not to + * decrease it. */ +int set_max_open_descriptors(int desired_max); + +/** Returns the open file descriptor limit for this process. + * @return the number of max open descriptors or 0 in case of failure. */ +int get_max_open_descriptors(); + +/* Maximize the open file descriptor limit for this process go up to the + max allowed */ +int max_sd(); + +#endif /* _NETUTIL_H_ */ diff --git a/libnetutil/npacket.h b/libnetutil/npacket.h new file mode 100644 index 0000000..49663ef --- /dev/null +++ b/libnetutil/npacket.h @@ -0,0 +1,93 @@ +/*************************************************************************** + * netutil.h -- The main include file exposing the external API for * + * libnetutil, a library that provides network-related functions or * + * classes that make it easier to handle things like network interfaces, * + * routing tables, raw packet manipulation, etc. The lib was originally * + * written for use in the Nmap Security Scanner ( https://nmap.org ). * + * * + ***********************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/ + * + ***************************************************************************/ + +/* $Id: npacket.h 18098 2010-06-14 11:50:12Z luis $ */ + +#ifndef __NPACKET_H__ +#define __NPACKET_H__ 1 + + +#include "ApplicationLayerElement.h" +#include "ARPHeader.h" +#include "DataLinkLayerElement.h" +#include "EthernetHeader.h" +#include "ICMPHeader.h" +#include "ICMPv4Header.h" +#include "ICMPv6Header.h" +#include "ICMPv6Option.h" +#include "ICMPv6RRBody.h" +#include "IPv4Header.h" +#include "IPv6Header.h" +#include "NetworkLayerElement.h" +#include "PacketElement.h" +#include "RawData.h" +#include "TCPHeader.h" +#include "TransportLayerElement.h" +#include "UDPHeader.h" +#include "HopByHopHeader.h" +#include "DestOptsHeader.h" +#include "FragmentHeader.h" +#include "RoutingHeader.h" +#include "PacketParser.h" + +#endif /* __NPACKET_H__ */ + |