diff options
Diffstat (limited to '')
-rw-r--r-- | tcpip.cc | 1838 |
1 files changed, 1838 insertions, 0 deletions
diff --git a/tcpip.cc b/tcpip.cc new file mode 100644 index 0000000..b4a1d1f --- /dev/null +++ b/tcpip.cc @@ -0,0 +1,1838 @@ + +/*************************************************************************** + * tcpip.cc -- Various functions relating to low level TCP/IP handling, * + * including sending raw packets, routing, printing packets, reading from * + * libpcap, 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/ + * + ***************************************************************************/ + +/* $Id$ */ + +#include "nmap.h" + +#include <locale.h> +#include "nbase.h" +#include <dnet.h> +#include "tcpip.h" +#include "NmapOps.h" +#include "Target.h" +#include "utils.h" +#include "nmap_error.h" +#include "libnetutil/netutil.h" + +#include "struct_ip.h" + +#if HAVE_NETINET_IF_ETHER_H +#ifndef NETINET_IF_ETHER_H +#include <netinet/if_ether.h> +#define NETINET_IF_ETHER_H +#endif /* NETINET_IF_ETHER_H */ +#endif /* HAVE_NETINET_IF_ETHER_H */ + +extern NmapOps o; + +static PacketCounter PktCt; + +/* Create a raw socket and do things that always apply to raw sockets: + * Set SO_BROADCAST. + * Set IP_HDRINCL. + * Bind to an interface with SO_BINDTODEVICE (if o.device is set). + The socket is created with address family AF_INET, but may be usable for + AF_INET6, depending on the operating system. */ +int nmap_raw_socket() { + int rawsd; + int one = 1; + + rawsd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + if (rawsd < 0) + return rawsd; + if (setsockopt (rawsd, SOL_SOCKET, SO_BROADCAST, (const char *) &one, sizeof(int)) != 0) { + error("Failed to secure socket broadcasting permission"); + perror("setsockopt"); + } +#ifndef WIN32 + sethdrinclude(rawsd); +#endif + socket_bindtodevice(rawsd, o.device); + + return rawsd; +} + +/* Fill buf (up to buflen -- truncate if necessary but always + terminate) with a short representation of the packet stats. + Returns buf. Aborts if there is a problem. */ +char *getFinalPacketStats(char *buf, int buflen) { + char sendbytesasc[16], recvbytesasc[16]; + + if (buflen <= 10 || !buf) + fatal("%s called with woefully inadequate parameters", __func__); + + Snprintf(buf, buflen, +#if WIN32 + "Raw packets sent: %I64u (%s) | Rcvd: %I64u (%s)", +#else + "Raw packets sent: %llu (%s) | Rcvd: %llu (%s)", +#endif + PktCt.sendPackets, + format_bytecount(PktCt.sendBytes, sendbytesasc, + sizeof(sendbytesasc)), PktCt.recvPackets, + format_bytecount(PktCt.recvBytes, recvbytesasc, + sizeof(recvbytesasc))); + return buf; +} + +/* Takes an ARP PACKET (not including ethernet header) and + prints it if packet tracing is enabled. The + direction must be PacketTrace::SENT or PacketTrace::RCVD . + Optional 'now' argument makes this function slightly more + efficient by avoiding a gettimeofday() call. */ +void PacketTrace::traceArp(pdirection pdir, const u8 *frame, u32 len, + struct timeval *now) { + struct timeval tv; + char arpdesc[128]; + char who_has[INET_ADDRSTRLEN], tell[INET_ADDRSTRLEN]; + + if (pdir == SENT) { + PktCt.sendPackets++; + PktCt.sendBytes += len; + } else { + PktCt.recvPackets++; + PktCt.recvBytes += len; + } + + if (!o.packetTrace()) + return; + + if (now) + tv = *now; + else + gettimeofday(&tv, NULL); + + if (len < 28) { + error("Packet tracer: Arp packets must be at least 28 bytes long. Should be exactly that length excl. ethernet padding."); + return; + } + + if (frame[7] == 1) { /* arp REQUEST */ + inet_ntop(AF_INET, (void *)(frame + 24), who_has, sizeof(who_has)); + inet_ntop(AF_INET, (void *)(frame + 14), tell, sizeof(tell)); + Snprintf(arpdesc, sizeof(arpdesc), "who-has %s tell %s", who_has, tell); + } else { /* ARP REPLY */ + inet_ntop(AF_INET, (void *)(frame + 14), who_has, sizeof(who_has)); + Snprintf(arpdesc, sizeof(arpdesc), + "reply %s is-at %02X:%02X:%02X:%02X:%02X:%02X", who_has, + frame[8], frame[9], frame[10], frame[11], frame[12], + frame[13]); + } + + log_write(LOG_STDOUT | LOG_NORMAL, "%s (%.4fs) ARP %s\n", + (pdir == SENT) ? "SENT" : "RCVD", + o.TimeSinceStart(&tv), arpdesc); + + return; +} + +/* Takes a Neighbor Discovery packet and prints it if packet tracing is + enabled. frame must point to the IPv6 header. */ +void PacketTrace::traceND(pdirection pdir, const u8 *frame, u32 len, + struct timeval *now) { + struct timeval tv; + const struct ip6_hdr *ip6; + const struct icmpv6_hdr *icmpv6; + const union icmpv6_msg *msg; + size_t msg_len; + const char *label; + char src[INET6_ADDRSTRLEN], dst[INET6_ADDRSTRLEN]; + char who_has[INET6_ADDRSTRLEN], tgt_is[INET6_ADDRSTRLEN]; + char desc[128]; + + if (pdir == SENT) { + PktCt.sendPackets++; + PktCt.sendBytes += len; + } else { + PktCt.recvPackets++; + PktCt.recvBytes += len; + } + + if (!o.packetTrace()) + return; + + if (now) + tv = *now; + else + gettimeofday(&tv, NULL); + + if (len < sizeof(*ip6) + sizeof(*icmpv6)) { + error("Packet tracer: ND packets must be at least %lu bytes long (is %lu).", + (unsigned long) (sizeof(*ip6) + sizeof(*icmpv6)), + (unsigned long) len); + return; + } + ip6 = (struct ip6_hdr *) frame; + icmpv6 = (struct icmpv6_hdr *) (frame + sizeof(*ip6)); + msg = (union icmpv6_msg *) (frame + sizeof(*ip6) + sizeof(*icmpv6)); + msg_len = frame + len - (u8 *) msg; + + if (icmpv6->icmpv6_type == ICMPV6_NEIGHBOR_SOLICITATION) { + label = "neighbor solicitation"; + if (msg_len < 20) { + Snprintf(desc, sizeof(desc), "packet too short"); + } else { + inet_ntop(AF_INET6, (void *)&msg->nd.icmpv6_target, who_has, sizeof(who_has)); + Snprintf(desc, sizeof(desc), "who has %s", who_has); + } + } else if (icmpv6->icmpv6_type == ICMPV6_NEIGHBOR_ADVERTISEMENT) { + label = "neighbor advertisement"; + if (msg_len < 28) { + Snprintf(desc, sizeof(desc), "packet too short"); + } else if (msg->nd.icmpv6_option_length == 0 || msg->nd.icmpv6_option_type != 2) { + /* We only handle target link-layer address in the first option. */ + Snprintf(desc, sizeof(desc), "no link-layer address"); + } else { + inet_ntop(AF_INET6, (void *)&msg->nd.icmpv6_target, tgt_is, sizeof(tgt_is)); + Snprintf(desc, sizeof(desc), "%s is at %s", + tgt_is, eth_ntoa(&msg->nd.icmpv6_mac)); + } + } else { + error("Unknown ICMPV6 type in %s.", __func__); + return; + } + + inet_ntop(AF_INET6, (void *)&ip6->ip6_src, src, sizeof(src)); + inet_ntop(AF_INET6, (void *)&ip6->ip6_dst, dst, sizeof(dst)); + log_write(LOG_STDOUT | LOG_NORMAL, "%s (%.4fs) %s %s > %s %s\n", + (pdir == SENT) ? "SENT" : "RCVD", + o.TimeSinceStart(&tv), label, src, dst, desc); + + return; +} + + +/* Returns a buffer of ASCII information about a 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". + IMPORTANT: This is a wrapper for function ippackethdrinfo(). Check + nbase/nbase_net.c for details on the returned buffer. */ +static const char *nmap_format_ippacket(const u8 *packet, u32 len) { + int detail = LOW_DETAIL; + if (o.debugging == 2) { + detail = MEDIUM_DETAIL; + } else if (o.debugging >= 3) { + detail = HIGH_DETAIL; + } + return ippackethdrinfo(packet, len, detail); +} + + + + +/* Takes an IP PACKET and prints it if packet tracing is enabled. + 'packet' must point to the IPv4 header. The direction must be + PacketTrace::SENT or PacketTrace::RCVD . Optional 'now' argument + makes this function slightly more efficient by avoiding a gettimeofday() + call. */ +void PacketTrace::trace(pdirection pdir, const u8 *packet, u32 len, + struct timeval *now) { + struct timeval tv; + + if (pdir == SENT) { + PktCt.sendPackets++; + PktCt.sendBytes += len; + } else { + PktCt.recvPackets++; + PktCt.recvBytes += len; + } + + if (!o.packetTrace()) + return; + + if (now) + tv = *now; + else + gettimeofday(&tv, NULL); + + if (len < 20) { + error("Packet tracer: tiny packet encountered"); + return; + } + + log_write(LOG_STDOUT | LOG_NORMAL, "%s (%.4fs) %s\n", + (pdir == SENT) ? "SENT" : "RCVD", + o.TimeSinceStart(&tv), nmap_format_ippacket(packet, len)); + + return; +} + +/* Adds a trace entry when a connect() is attempted if packet tracing + is enabled. Pass IPPROTO_TCP or IPPROTO_UDP as the protocol. The + sock may be a sockaddr_in or sockaddr_in6. The return code of + connect is passed in connectrc. If the return code is -1, get the + errno and pass that as connect_errno. */ +void PacketTrace::traceConnect(u8 proto, const struct sockaddr *sock, + int socklen, int connectrc, + int connect_errno, + const struct timeval *now) { + const struct sockaddr_in *sin = (struct sockaddr_in *) sock; +#if HAVE_IPV6 + const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sock; +#endif + struct timeval tv; + char errbuf[64] = ""; + char targetipstr[INET6_ADDRSTRLEN] = ""; + u16 targetport = 0; + + if (!o.packetTrace()) + return; + + if (now) + tv = *now; + else + gettimeofday(&tv, NULL); + + assert(proto == IPPROTO_TCP || proto == IPPROTO_UDP); + + if (connectrc == 0) { + Strncpy(errbuf, "Connected", sizeof(errbuf)); + } +#if WIN32 + else if (connect_errno == WSAEWOULDBLOCK) { + /* Special case for WSAEWOULDBLOCK. socket_strerror returns the unwieldy + "A non-blocking socket operation could not be completed immediately." */ + Strncpy(errbuf, "Operation now in progress", sizeof(errbuf)); + } +#endif + else { + Snprintf(errbuf, sizeof(errbuf), "%s", socket_strerror(connect_errno)); + } + + if (sin->sin_family == AF_INET) { + if (inet_ntop(sin->sin_family, (char *) &sin->sin_addr, targetipstr, + sizeof(targetipstr)) == NULL) + fatal("Failed to convert target IPv4 address to presentation format!?!"); + targetport = ntohs(sin->sin_port); + } else { +#if HAVE_IPV6 + assert(sin->sin_family == AF_INET6); + if (inet_ntop(sin->sin_family, (char *) &sin6->sin6_addr, targetipstr, + sizeof(targetipstr)) == NULL) + fatal("Failed to convert target IPv6 address to presentation format!?!"); + targetport = ntohs(sin6->sin6_port); +#else + assert(0); +#endif + } + + log_write(LOG_STDOUT | LOG_NORMAL, + "CONN (%.4fs) %s localhost > %s:%d => %s\n", + o.TimeSinceStart(&tv), + (proto == IPPROTO_TCP) ? "TCP" : "UDP", targetipstr, + targetport, errbuf); +} + +/* Converts an IP address given in a sockaddr_storage to an IPv4 or + IPv6 IP address string. Since a static buffer is returned, this is + not thread-safe and can only be used once in calls like printf() */ +const char *inet_socktop(const struct sockaddr_storage *ss) { + static char buf[INET6_ADDRSTRLEN]; + const struct sockaddr_in *sin = (struct sockaddr_in *) ss; +#if HAVE_IPV6 + const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) ss; +#endif + + if (inet_ntop(sin->sin_family, (sin->sin_family == AF_INET) ? + (char *) &sin->sin_addr : +#if HAVE_IPV6 + (char *) &sin6->sin6_addr, +#else + (char *) NULL, +#endif /* HAVE_IPV6 */ + buf, sizeof(buf)) == NULL) { + fatal("Failed to convert target address to presentation format in %s!?! Error: %s", __func__, strerror(socket_errno())); + } + return buf; +} + +/* Tries to resolve the given name (or literal IP) into a sockaddr structure. + This function calls getaddrinfo and returns the same addrinfo linked list + that getaddrinfo produces. Returns NULL for any error or failure to resolve. + You need to call freeaddrinfo on the result if non-NULL. */ +struct addrinfo *resolve_all(const char *hostname, int pf) { + struct addrinfo hints; + struct addrinfo *result; + int rc; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = pf; + /* Otherwise we get multiple identical addresses with different socktypes. */ + hints.ai_socktype = SOCK_DGRAM; +#ifdef AI_IDN + /* Try resolving internationalized domain names */ + hints.ai_flags = AI_IDN; + setlocale(LC_CTYPE, ""); +#endif + rc = getaddrinfo(hostname, NULL, &hints, &result); +#ifdef AI_IDN + setlocale(LC_CTYPE, o.locale); +#endif + if (rc != 0){ + if (o.debugging > 1) + error("Error resolving %s: %s", hostname, gai_strerror(rc)); + return NULL; + } + + return result; +} + + +/* Send a pre-built IPv4 packet. Handles fragmentation and whether to send with + an ethernet handle or a socket. */ +static int send_ipv4_packet(int sd, const struct eth_nfo *eth, + const struct sockaddr_in *dst, + const u8 *packet, unsigned int packetlen) { + const struct ip *ip = (struct ip *) packet; + int res; + + assert(packet); + assert((int) packetlen > 0); + + /* Fragmentation requested && packet is bigger than MTU */ + if (o.fragscan && !(ntohs(ip->ip_off) & IP_DF) && + (packetlen - ip->ip_hl * 4 > (unsigned int) o.fragscan)) { + res = send_frag_ip_packet(sd, eth, dst, packet, packetlen, o.fragscan); + } else { + res = send_ip_packet_eth_or_sd(sd, eth, dst, packet, packetlen); + } + if (res != -1) + PacketTrace::trace(PacketTrace::SENT, packet, packetlen); + + return res; +} + +static int send_ipv6_packet(int sd, const struct eth_nfo *eth, + const struct sockaddr_in6 *dst, + const u8 *packet, unsigned int packetlen) { + int res; + + res = send_ipv6_packet_eth_or_sd(sd, eth, dst, packet, packetlen); + if (res != -1) + PacketTrace::trace(PacketTrace::SENT, packet, packetlen); + + return res; +} + +int send_ip_packet(int sd, const struct eth_nfo *eth, + const struct sockaddr_storage *dst, + const u8 *packet, unsigned int packetlen) { + const struct ip *ip = (struct ip *) packet; + + /* Ensure there's enough to read ip->ip_v at least. */ + if (packetlen < 1) + return -1; + + if (ip->ip_v == 4) { + assert(dst->ss_family == AF_INET); + return send_ipv4_packet(sd, eth, (struct sockaddr_in *) dst, packet, packetlen); + } else if (ip->ip_v == 6) { + assert(dst->ss_family == AF_INET6); + return send_ipv6_packet(sd, eth, (struct sockaddr_in6 *) dst, packet, packetlen); + } + + fatal("%s only understands IP versions 4 and 6 (got %u)", __func__, ip->ip_v); +} + + +/* Return an IPv4 pseudoheader checksum for the given protocol and data. Unlike + ipv4_pseudoheader_cksum, this knows about STUPID_SOLARIS_CHECKSUM_BUG and + takes care of o.badsum. */ +static u16 ipv4_cksum(const struct in_addr *src, const struct in_addr *dst, + u8 proto, const void *data, u16 len) { + u16 sum; + +#if STUPID_SOLARIS_CHECKSUM_BUG + sum = len; +#else + sum = ipv4_pseudoheader_cksum(src, dst, proto, len, data); +#endif + + if (o.badsum) { + --sum; + if (proto == IPPROTO_UDP && sum == 0) + sum = 0xffff; // UDP checksum=0 means no checksum + } + + return sum; +} + +/* Return an IPv6 pseudoheader checksum for the given protocol and data. Unlike + ipv6_pseudoheader_cksum, this takes care of o.badsum. */ +static u16 ipv6_cksum(const struct in6_addr *src, const struct in6_addr *dst, + u8 nxt, const void *data, u16 len) { + u16 sum; + + sum = ipv6_pseudoheader_cksum(src, dst, nxt, len, data); + + if (o.badsum) { + --sum; + if (nxt == IPPROTO_UDP && sum == 0) + sum = 0xffff; // UDP checksum=0 means no checksum + } + + return sum; +} + +// fill ip header. no error check. +// This function is also changing what's needed from host to network order. +static inline int fill_ip_raw(struct ip *ip, int packetlen, const u8 *ipopt, + int ipoptlen, int tos, int id, + int off, int ttl, int p, + const struct in_addr *ip_src, + const struct in_addr *ip_dst) { + ip->ip_v = 4; + ip->ip_hl = 5 + (ipoptlen / 4); + ip->ip_tos = tos; + ip->ip_len = htons(packetlen); + ip->ip_id = htons(id); + ip->ip_off = htons(off); + ip->ip_ttl = ttl; + ip->ip_p = p; + ip->ip_src.s_addr = ip_src->s_addr; + ip->ip_dst.s_addr = ip_dst->s_addr; + + if (ipoptlen) + memcpy((u8 *) ip + sizeof(struct ip), ipopt, ipoptlen); + + // ip options source routing hack: + if (ipoptlen && o.ipopt_firsthop && o.ipopt_lasthop) { + u8 *ipo = (u8 *) ip + sizeof(struct ip); + struct in_addr *newdst = (struct in_addr *) &ipo[o.ipopt_firsthop]; + struct in_addr *olddst = (struct in_addr *) &ipo[o.ipopt_lasthop]; + // our destination is somewhere else :) + ip->ip_dst.s_addr = newdst->s_addr; + + // and last hop should be destination + olddst->s_addr = ip_dst->s_addr; + } + +#if HAVE_IP_IP_SUM + ip->ip_sum = 0; + ip->ip_sum = in_cksum((unsigned short *) ip, sizeof(struct ip) + ipoptlen); +#endif + return (sizeof(struct ip) + ipoptlen); +} + +/* Builds an IP packet (including an IP header) by packing the fields + with the given information. It allocates a new buffer to store the + packet contents, and then returns that buffer. The packet is not + actually sent by this function. Caller must delete the buffer when + finished with the packet. The packet length is returned in + packetlen, which must be a valid int pointer. */ +u8 *build_ip_raw(const struct in_addr *source, + const struct in_addr *victim, u8 proto, int ttl, + u16 ipid, u8 tos, bool df, const u8 *ipopt, int ipoptlen, + const char *data, u16 datalen, u32 *outpacketlen) { + int packetlen = sizeof(struct ip) + ipoptlen + datalen; + u8 *packet = (u8 *) safe_malloc(packetlen); + struct ip *ip = (struct ip *) packet; + static int myttl = 0; + + /* check that required fields are there and not too silly */ + assert(source); + assert(victim); + assert(ipoptlen % 4 == 0); + + /* Time to live */ + if (ttl == -1) { + myttl = (get_random_uint() % 23) + 37; + } else { + myttl = ttl; + } + + fill_ip_raw(ip, packetlen, ipopt, ipoptlen, + tos, ipid, df ? IP_DF : 0, myttl, proto, source, victim); + + /* We should probably copy the data over too */ + if (data && datalen) + memcpy((u8 *) ip + sizeof(struct ip) + ipoptlen, data, datalen); + + *outpacketlen = packetlen; + return packet; +} + +u8 *build_ipv6_raw(const struct in6_addr *source, + const struct in6_addr *victim, u8 tc, u32 flowlabel, + u8 nextheader, int hoplimit, + const char *data, u16 datalen, u32 *outpacketlen) { + u8 *packet; + + assert(source != NULL); + assert(victim != NULL); + + if (hoplimit == -1) + hoplimit = (get_random_uint() % 23) + 37; + + *outpacketlen = sizeof(struct ip6_hdr) + datalen; + packet = (u8 *) safe_malloc(*outpacketlen); + + ip6_pack_hdr(packet, tc, flowlabel, datalen, nextheader, hoplimit, *source, *victim); + memcpy(packet + sizeof(struct ip6_hdr), data, datalen); + + return packet; +} + + +/* Build a TCP packet (no IP header). Sets tcp->th_sum to 0 so it can be filled + in by a function with knowledge of the higher-level pseudoheader. */ +static u8 *build_tcp(u16 sport, u16 dport, u32 seq, u32 ack, u8 reserved, + u8 flags, u16 window, u16 urp, + const u8 *tcpopt, int tcpoptlen, + const char *data, u16 datalen, u32 *packetlen) { + struct tcp_hdr *tcp; + u8 *packet; + + if (tcpoptlen % 4 != 0) + fatal("%s called with an option length argument of %d which is illegal because it is not divisible by 4. Just add \\0 padding to the end.", __func__, tcpoptlen); + + *packetlen = sizeof(*tcp) + tcpoptlen + datalen; + packet = (u8 *) safe_malloc(*packetlen); + tcp = (struct tcp_hdr *) packet; + + memset(tcp, 0, sizeof(*tcp)); + tcp->th_sport = htons(sport); + tcp->th_dport = htons(dport); + + if (seq) + tcp->th_seq = htonl(seq); + else if (flags & TH_SYN) + get_random_bytes(&(tcp->th_seq), 4); + + if (ack) + tcp->th_ack = htonl(ack); + + if (reserved) + tcp->th_x2 = reserved & 0x0F; + tcp->th_off = 5 + (tcpoptlen / 4); /* words */ + tcp->th_flags = flags; + + if (window) + tcp->th_win = htons(window); + else + tcp->th_win = htons(1024); /* Who cares */ + + if (urp) + tcp->th_urp = htons(urp); + + /* And the options */ + if (tcpoptlen) + memcpy(packet + sizeof(*tcp), tcpopt, tcpoptlen); + + /* We should probably copy the data over too */ + if (data && datalen) + memcpy(packet + sizeof(*tcp) + tcpoptlen, data, datalen); + + tcp->th_sum = 0; + + return packet; +} + +/* Builds a TCP packet (including an IP header) by packing the fields + with the given information. It allocates a new buffer to store the + packet contents, and then returns that buffer. The packet is not + actually sent by this function. Caller must delete the buffer when + finished with the packet. The packet length is returned in + packetlen, which must be a valid int pointer. */ +u8 *build_tcp_raw(const struct in_addr *source, + const struct in_addr *victim, int ttl, u16 ipid, u8 tos, + bool df, const u8 *ipopt, int ipoptlen, u16 sport, u16 dport, + u32 seq, u32 ack, u8 reserved, u8 flags, u16 window, + u16 urp, const u8 *tcpopt, int tcpoptlen, const char *data, + u16 datalen, u32 *packetlen) { + struct tcp_hdr *tcp; + u32 tcplen; + u8 *ip; + + tcp = (struct tcp_hdr *) build_tcp(sport, dport, seq, ack, reserved, flags, + window, urp, tcpopt, tcpoptlen, data, datalen, &tcplen); + tcp->th_sum = ipv4_cksum(source, victim, IPPROTO_TCP, tcp, tcplen); + ip = build_ip_raw(source, victim, IPPROTO_TCP, ttl, ipid, tos, df, + ipopt, ipoptlen, (char *) tcp, tcplen, packetlen); + free(tcp); + + return ip; +} + +/* Builds a TCP packet (including an IPv6 header) by packing the fields + with the given information. It allocates a new buffer to store the + packet contents, and then returns that buffer. The packet is not + actually sent by this function. Caller must delete the buffer when + finished with the packet. The packet length is returned in + packetlen, which must be a valid int pointer. */ +u8 *build_tcp_raw_ipv6(const struct in6_addr *source, + const struct in6_addr *victim, u8 tc, u32 flowlabel, + u8 hoplimit, u16 sport, u16 dport, u32 seq, u32 ack, + u8 reserved, u8 flags, u16 window, u16 urp, + const u8 *tcpopt, int tcpoptlen, const char *data, + u16 datalen, u32 *packetlen) { + struct tcp_hdr *tcp; + u32 tcplen; + u8 *ipv6; + + tcp = (struct tcp_hdr *) build_tcp(sport, dport, seq, ack, reserved, flags, + window, urp, tcpopt, tcpoptlen, data, datalen, &tcplen); + tcp->th_sum = ipv6_cksum(source, victim, IPPROTO_TCP, tcp, tcplen); + ipv6 = build_ipv6_raw(source, victim, tc, flowlabel, IPPROTO_TCP, hoplimit, + (char *) tcp, tcplen, packetlen); + free(tcp); + + return ipv6; +} + +/* You need to call sethdrinclude(sd) on the sending sd before calling this */ +int send_tcp_raw(int sd, const struct eth_nfo *eth, + const struct in_addr *source, + const struct in_addr *victim, int ttl, bool df, + u8 *ipops, int ipoptlen, u16 sport, u16 dport, u32 seq, + u32 ack, u8 reserved, u8 flags, u16 window, u16 urp, + u8 *options, int optlen, const char *data, u16 datalen) { + struct sockaddr_storage dst; + struct sockaddr_in *dst_in; + unsigned int packetlen; + int res = -1; + + u8 *packet = build_tcp_raw(source, victim, + ttl, get_random_u16(), IP_TOS_DEFAULT, df, + ipops, ipoptlen, + sport, dport, + seq, ack, reserved, flags, window, urp, + options, optlen, + data, datalen, &packetlen); + if (!packet) + return -1; + memset(&dst, 0, sizeof(dst)); + dst_in = (struct sockaddr_in *) &dst; + dst_in->sin_family = AF_INET; + dst_in->sin_addr = *victim; + res = send_ip_packet(sd, eth, &dst, packet, packetlen); + + free(packet); + return res; +} + +int send_tcp_raw_decoys(int sd, const struct eth_nfo *eth, + const struct in_addr *victim, + int ttl, bool df, + u8 *ipopt, int ipoptlen, + u16 sport, u16 dport, + u32 seq, u32 ack, u8 reserved, u8 flags, + u16 window, u16 urp, u8 *options, int optlen, + const char *data, u16 datalen) { + int decoy; + + for (decoy = 0; decoy < o.numdecoys; decoy++) + if (send_tcp_raw(sd, eth, + &((struct sockaddr_in *)&o.decoys[decoy])->sin_addr, victim, + ttl, df, + ipopt, ipoptlen, + sport, dport, + seq, ack, reserved, flags, window, urp, + options, optlen, data, datalen) == -1) + return -1; + + return 0; +} + + +/* Build a UDP packet (no IP header). Sets udp->uh_sum to 0 so it can be filled + in by a function with knowledge of the higher-level pseudoheader. */ +static u8 *build_udp(u16 sport, u16 dport, const char *data, u16 datalen, + u32 *packetlen) { + struct udp_hdr *udp; + u8 *packet; + + *packetlen = sizeof(*udp) + datalen; + packet = (u8 *) safe_malloc(*packetlen); + udp = (struct udp_hdr *) packet; + + memset(udp, 0, sizeof(*udp)); + udp->uh_sport = htons(sport); + udp->uh_dport = htons(dport); + + udp->uh_ulen = htons(*packetlen); + if (data && datalen) + memcpy(packet + sizeof(*udp), data, datalen); + + udp->uh_sum = 0; + + return packet; +} + +/* Builds a UDP packet (including an IP header) by packing the fields + with the given information. It allocates a new buffer to store the + packet contents, and then returns that buffer. The packet is not + actually sent by this function. Caller must delete the buffer when + finished with the packet. The packet length is returned in + packetlen, which must be a valid int pointer. */ +u8 *build_udp_raw(const struct in_addr *source, const struct in_addr *victim, + int ttl, u16 ipid, u8 tos, bool df, + u8 *ipopt, int ipoptlen, + u16 sport, u16 dport, + const char *data, u16 datalen, u32 *packetlen) { + struct udp_hdr *udp; + u32 udplen; + u8 *ip; + + udp = (struct udp_hdr *) build_udp(sport, dport, data, datalen, &udplen); + udp->uh_sum = ipv4_cksum(source, victim, IPPROTO_UDP, udp, udplen); + ip = build_ip_raw(source, victim, IPPROTO_UDP, ttl, ipid, tos, df, + ipopt, ipoptlen, (char *) udp, udplen, packetlen); + free(udp); + + return ip; +} + +/* Builds a UDP packet (including an IPv6 header) by packing the fields + with the given information. It allocates a new buffer to store the + packet contents, and then returns that buffer. The packet is not + actually sent by this function. Caller must delete the buffer when + finished with the packet. The packet length is returned in + packetlen, which must be a valid int pointer. */ +u8 *build_udp_raw_ipv6(const struct in6_addr *source, + const struct in6_addr *victim, u8 tc, u32 flowlabel, + u8 hoplimit, u16 sport, u16 dport, + const char *data, u16 datalen, u32 *packetlen) { + struct udp_hdr *udp; + u32 udplen; + u8 *ipv6; + + udp = (struct udp_hdr *) build_udp(sport, dport, data, datalen, &udplen); + udp->uh_sum = ipv6_cksum(source, victim, IPPROTO_UDP, udp, udplen); + ipv6 = build_ipv6_raw(source, victim, tc, flowlabel, IPPROTO_UDP, hoplimit, + (char *) udp, udplen, packetlen); + free(udp); + + return ipv6; +} + +int send_udp_raw(int sd, const struct eth_nfo *eth, + struct in_addr *source, const struct in_addr *victim, + int ttl, u16 ipid, + u8 *ipopt, int ipoptlen, + u16 sport, u16 dport, const char *data, u16 datalen) { + struct sockaddr_storage dst; + struct sockaddr_in *dst_in; + unsigned int packetlen; + int res = -1; + u8 *packet = build_udp_raw(source, victim, + ttl, ipid, IP_TOS_DEFAULT, false, + ipopt, ipoptlen, + sport, dport, + data, datalen, &packetlen); + if (!packet) + return -1; + memset(&dst, 0, sizeof(dst)); + dst_in = (struct sockaddr_in *) &dst; + dst_in->sin_family = AF_INET; + dst_in->sin_addr = *victim; + res = send_ip_packet(sd, eth, &dst, packet, packetlen); + + free(packet); + return res; +} + +int send_udp_raw_decoys(int sd, const struct eth_nfo *eth, + const struct in_addr *victim, + int ttl, u16 ipid, + u8 *ipops, int ipoptlen, + u16 sport, u16 dport, const char *data, u16 datalen) { + int decoy; + + for (decoy = 0; decoy < o.numdecoys; decoy++) + if (send_udp_raw(sd, eth, &((struct sockaddr_in *)&o.decoys[decoy])->sin_addr, victim, + ttl, ipid, ipops, ipoptlen, + sport, dport, data, datalen) == -1) + return -1; + + return 0; +} + + +/* Build an SCTP packet (no IP header). */ +static u8 *build_sctp(u16 sport, u16 dport, u32 vtag, + const char *chunks, int chunkslen, + const char *data, u16 datalen, + u32 *packetlen) { + struct sctp_hdr *sctp; + u8 *packet; + + *packetlen = sizeof(*sctp) + chunkslen + datalen; + packet = (u8 *) safe_malloc(*packetlen); + sctp = (struct sctp_hdr *) packet; + + sctp->sh_sport = htons(sport); + sctp->sh_dport = htons(dport); + sctp->sh_sum = 0; + sctp->sh_vtag = htonl(vtag); + + if (chunks) + memcpy(packet + sizeof(*sctp), chunks, chunkslen); + + if (data) + memcpy(packet + sizeof(*sctp) + chunkslen, data, datalen); + + /* RFC 2960 originally defined Adler32 checksums, which was later + * revised to CRC32C in RFC 3309 and RFC 4960 respectively. + * Nmap uses CRC32C by default, unless --adler32 is given. */ + if (o.adler32) + sctp->sh_sum = htonl(nbase_adler32(packet, *packetlen)); + else + sctp->sh_sum = htonl(nbase_crc32c(packet, *packetlen)); + + if (o.badsum) + --sctp->sh_sum; + + return packet; +} + +/* Builds an SCTP packet (including an IP header) by packing the fields + with the given information. It allocates a new buffer to store the + packet contents, and then returns that buffer. The packet is not + actually sent by this function. Caller must delete the buffer when + finished with the packet. The packet length is returned in + packetlen, which must be a valid int pointer. */ +u8 *build_sctp_raw(const struct in_addr *source, + const struct in_addr *victim, int ttl, u16 ipid, + u8 tos, bool df, u8 *ipopt, int ipoptlen, u16 sport, + u16 dport, u32 vtag, char *chunks, int chunkslen, + const char *data, u16 datalen, u32 *packetlen) { + u8 *ip, *sctp; + u32 sctplen; + + sctp = build_sctp(sport, dport, vtag, chunks, chunkslen, data, datalen, &sctplen); + ip = build_ip_raw(source, victim, IPPROTO_SCTP, ttl, ipid, tos, df, + ipopt, ipoptlen, (char *) sctp, sctplen, packetlen); + free(sctp); + + return ip; +} + +u8 *build_sctp_raw_ipv6(const struct in6_addr *source, + const struct in6_addr *victim, u8 tc, u32 flowlabel, + u8 hoplimit, u16 sport, u16 dport, u32 vtag, + char *chunks, int chunkslen, const char *data, u16 datalen, + u32 *packetlen) { + u8 *ipv6, *sctp; + u32 sctplen; + + sctp = build_sctp(sport, dport, vtag, chunks, chunkslen, data, datalen, &sctplen); + ipv6 = build_ipv6_raw(source, victim, tc, flowlabel, IPPROTO_SCTP, hoplimit, + (char *) sctp, sctplen, packetlen); + free(sctp); + + return ipv6; +} + + +/* Builds an ICMP packet (including an IP header) by packing the fields + with the given information. It allocates a new buffer to store the + packet contents, and then returns that buffer. The packet is not + actually sent by this function. Caller must delete the buffer when + finished with the packet. The packet length is returned in + packetlen, which must be a valid int pointer. The id/seq will be converted + to network byte order (if it differs from HBO) */ +u8 *build_icmp_raw(const struct in_addr *source, + const struct in_addr *victim, int ttl, u16 ipid, + u8 tos, bool df, u8 *ipopt, int ipoptlen, u16 seq, + unsigned short id, u8 ptype, u8 pcode, const char *data, + u16 datalen, u32 *packetlen) { + struct ppkt { + u8 type; + u8 code; + u16 checksum; + u16 id; + u16 seq; + u8 data[1500]; /* Note -- first 4-12 bytes can be used for ICMP header */ + } pingpkt; + u8 *datastart = pingpkt.data; + /* dlen is the amount of space remaining in the data buffer; it may be reduced + depending on type. */ + int dlen = sizeof(pingpkt.data); + int icmplen = 0; + char *ping = (char *) &pingpkt; + + pingpkt.type = ptype; + pingpkt.code = pcode; + + if (ptype == 8) { + /* echo request */ + icmplen = 8; + } else if (ptype == 13 && pcode == 0) { + /* ICMP timestamp req */ + icmplen = 20; + memset(datastart, 0, 12); + datastart += 12; + dlen -= 12; + } else if (ptype == 17 && pcode == 0) { + /* icmp netmask req */ + icmplen = 12; + memset(datastart, 0, 4); + datastart += 4; + dlen -= 4; + } else { + fatal("Unknown icmp type/code (%d/%d) in %s", ptype, pcode, __func__); + } + + /* Copy the data over too */ + if (datalen > 0) { + icmplen += MIN(dlen, datalen); + if (data == NULL) + memset(datastart, 0, MIN(dlen, datalen)); + else + memcpy(datastart, data, MIN(dlen, datalen)); + } + + /* Fill out the ping packet. All the ICMP types handled by this function have + the id and seq fields. */ + pingpkt.id = htons(id); + pingpkt.seq = htons(seq); + pingpkt.checksum = 0; + pingpkt.checksum = in_cksum((unsigned short *) ping, icmplen); + + if (o.badsum) + --pingpkt.checksum; + + return build_ip_raw(source, victim, IPPROTO_ICMP, ttl, ipid, tos, df, + ipopt, ipoptlen, ping, icmplen, packetlen); +} + + +/* Builds an ICMPv6 packet (including an IPv6 header). */ +u8 *build_icmpv6_raw(const struct in6_addr *source, + const struct in6_addr *victim, u8 tc, u32 flowlabel, + u8 hoplimit, u16 seq, u16 id, u8 ptype, u8 pcode, + const char *data, u16 datalen, u32 *packetlen) { + char *packet; + struct icmpv6_hdr *icmpv6; + union icmpv6_msg *msg; + unsigned int icmplen; + u8 *ipv6; + + packet = (char *) safe_malloc(sizeof(*icmpv6) + sizeof(*msg) + datalen); + icmpv6 = (struct icmpv6_hdr *) packet; + msg = (union icmpv6_msg *) (packet + sizeof(*icmpv6)); + + icmplen = sizeof(*icmpv6); + icmpv6->icmpv6_type = ptype; + icmpv6->icmpv6_code = pcode; + + if (ptype == ICMPV6_ECHO) { + msg->echo.icmpv6_seq = htons(seq); + msg->echo.icmpv6_id = htons(id); + icmplen += sizeof(msg->echo); + } + + /* At this point icmplen <= sizeof(*icmpv6) + sizeof(*msg). */ + memcpy(packet + icmplen, data, datalen); + icmplen += datalen; + + icmpv6->icmpv6_cksum = 0; + icmpv6->icmpv6_cksum = ipv6_pseudoheader_cksum(source, victim, + IPPROTO_ICMPV6, icmplen, icmpv6); + if (o.badsum) + icmpv6->icmpv6_cksum--; + + ipv6 = build_ipv6_raw(source, victim, tc, flowlabel, IPPROTO_ICMPV6, hoplimit, + packet, icmplen, packetlen); + + free(packet); + return ipv6; +} + +/* Builds an IGMP packet (including an IP header) by packing the fields + with the given information. It allocates a new buffer to store the + packet contents, and then returns that buffer. The packet is not + actually sent by this function. Caller must delete the buffer when + finished with the packet. The packet length is returned in packetlen, + which must be a valid int pointer. */ +u8 *build_igmp_raw(const struct in_addr *source, + const struct in_addr *victim, int ttl, u16 ipid, + u8 tos, bool df, u8 *ipopt, int ipoptlen, u8 ptype, + u8 pcode, const char *data, u16 datalen, u32 *packetlen) { + struct { + u8 igmp_type; + u8 igmp_code; + u16 igmp_cksum; + u32 var; /* changes between types, unused. usually group address. */ + u8 data[1500]; + } igmp; + u32 *datastart = (u32 *) igmp.data; + int dlen = sizeof(igmp.data); + int igmplen = 0; + char *pkt = (char *) &igmp; + + igmp.igmp_type = ptype; + igmp.igmp_code = pcode; + + if (ptype == 0x11) { + /* Membership Query */ + igmplen = 8; + } else if (ptype == 0x12) { + /* v1 Membership Report */ + igmplen = 8; + } else if (ptype == 0x16) { + /* v2 Membership Report */ + igmplen = 8; + } else if (ptype == 0x17) { + /* v2 Leave Group */ + igmplen = 8; + } else if (ptype == 0x22) { + /* v3 Membership Report */ + igmplen = 8; + } else { + fatal("Unknown igmp type (%d) in %s", ptype, __func__); + } + + if (datalen > 0) { + igmplen += MIN(dlen, datalen); + if (data == NULL) + memset(datastart, 0, MIN(dlen, datalen)); + else + memcpy(datastart, data, MIN(dlen, datalen)); + } + + igmp.igmp_cksum = 0; + igmp.igmp_cksum = in_cksum((unsigned short *) pkt, igmplen); + + if (o.badsum) + --igmp.igmp_cksum; + + return build_ip_raw(source, victim, IPPROTO_IGMP, ttl, ipid, tos, df, + ipopt, ipoptlen, pkt, igmplen, packetlen); +} + + +/* A simple function I wrote to help in debugging, shows the important fields + of a TCP packet*/ +int readtcppacket(const u8 *packet, int readdata) { + + const struct ip *ip = (struct ip *) packet; + const struct tcp_hdr *tcp = (struct tcp_hdr *) (packet + sizeof(struct ip)); + const unsigned char *data = packet + sizeof(struct ip) + sizeof(struct tcp_hdr); + int tot_len; + struct in_addr bullshit, bullshit2; + char sourcehost[16]; + int i; + int realfrag = 0; + + if (!packet) { + error("%s: packet is NULL!", __func__); + return -1; + } + + bullshit.s_addr = ip->ip_src.s_addr; + bullshit2.s_addr = ip->ip_dst.s_addr; + realfrag = htons(ntohs(ip->ip_off) & IP_OFFMASK); + tot_len = htons(ip->ip_len); + strncpy(sourcehost, inet_ntoa(bullshit), 16); + i = 4 * (ntohs(ip->ip_hl) + ntohs(tcp->th_off)); + if (ip->ip_p == IPPROTO_TCP) { + if (realfrag) + log_write(LOG_PLAIN, "Packet is fragmented, offset field: %u\n", + realfrag); + else { + log_write(LOG_PLAIN, + "TCP packet: %s:%d -> %s:%d (total: %d bytes)\n", + sourcehost, ntohs(tcp->th_sport), inet_ntoa(bullshit2), + ntohs(tcp->th_dport), tot_len); + log_write(LOG_PLAIN, "Flags: "); + if (!tcp->th_flags) + log_write(LOG_PLAIN, "(none)"); + if (tcp->th_flags & TH_RST) + log_write(LOG_PLAIN, "RST "); + if (tcp->th_flags & TH_SYN) + log_write(LOG_PLAIN, "SYN "); + if (tcp->th_flags & TH_ACK) + log_write(LOG_PLAIN, "ACK "); + if (tcp->th_flags & TH_PUSH) + log_write(LOG_PLAIN, "PSH "); + if (tcp->th_flags & TH_FIN) + log_write(LOG_PLAIN, "FIN "); + if (tcp->th_flags & TH_URG) + log_write(LOG_PLAIN, "URG "); + log_write(LOG_PLAIN, "\n"); + + log_write(LOG_PLAIN, "ipid: %hu ttl: %hhu ", ntohs(ip->ip_id), + ip->ip_ttl); + + if (tcp->th_flags & (TH_SYN | TH_ACK)) + log_write(LOG_PLAIN, "Seq: %u\tAck: %u\n", + (unsigned int) ntohl(tcp->th_seq), + (unsigned int) ntohl(tcp->th_ack)); + else if (tcp->th_flags & TH_SYN) + log_write(LOG_PLAIN, "Seq: %u\n", + (unsigned int) ntohl(tcp->th_seq)); + else if (tcp->th_flags & TH_ACK) + log_write(LOG_PLAIN, "Ack: %u\n", + (unsigned int) ntohl(tcp->th_ack)); + } + } + if (readdata && i < tot_len) { + log_write(LOG_PLAIN, "Data portion:\n"); + while (i < tot_len) { + log_write(LOG_PLAIN, "%2X%c", data[i], ((i + 1) % 16) ? ' ' : '\n'); + i++; + } + log_write(LOG_PLAIN, "\n"); + } + + return 0; +} + +/* A simple function I wrote to help in debugging, shows the important fields + of a UDP packet*/ +int readudppacket(const u8 *packet, int readdata) { + const struct ip *ip = (struct ip *) packet; + const struct udp_hdr *udp = (struct udp_hdr *) (packet + sizeof(struct ip)); + const unsigned char *data = packet + sizeof(struct ip) + sizeof(struct udp_hdr); + int tot_len; + struct in_addr bullshit, bullshit2; + char sourcehost[16]; + int i; + int realfrag = 0; + + if (!packet) { + error("%s: packet is NULL!", __func__); + return -1; + } + + bullshit.s_addr = ip->ip_src.s_addr; + bullshit2.s_addr = ip->ip_dst.s_addr; + realfrag = htons(ntohs(ip->ip_off) & IP_OFFMASK); + tot_len = htons(ip->ip_len); + strncpy(sourcehost, inet_ntoa(bullshit), 16); + i = 4 * (ntohs(ip->ip_hl)) + 8; + if (ip->ip_p == IPPROTO_UDP) { + if (realfrag) + log_write(LOG_PLAIN, "Packet is fragmented, offset field: %u\n", + realfrag); + else { + log_write(LOG_PLAIN, + "UDP packet: %s:%d -> %s:%d (total: %d bytes)\n", + sourcehost, ntohs(udp->uh_sport), inet_ntoa(bullshit2), + ntohs(udp->uh_dport), tot_len); + + log_write(LOG_PLAIN, "ttl: %hhu ", ip->ip_ttl); + } + } + if (readdata && i < tot_len) { + log_write(LOG_PLAIN, "Data portion:\n"); + while (i < tot_len) { + log_write(LOG_PLAIN, "%2X%c", data[i], ((i + 1) % 16) ? ' ' : '\n'); + i++; + } + log_write(LOG_PLAIN, "\n"); + } + return 0; +} + + +/* Used by validatepkt() to validate the TCP header (including option lengths). + The options checked are MSS, WScale, SackOK, Sack, and Timestamp. */ +static bool validateTCPhdr(const u8 *tcpc, unsigned len) { + const struct tcp_hdr *tcp = (struct tcp_hdr *) tcpc; + unsigned hdrlen, optlen; + + hdrlen = tcp->th_off * 4; + + /* Check header length */ + if (hdrlen > len || hdrlen < sizeof(struct tcp_hdr)) + return false; + + /* Get to the options */ + tcpc += sizeof(struct tcp_hdr); + optlen = hdrlen - sizeof(struct tcp_hdr); + +// This macro guarantees optlen does not underflow by returning if optlen < expected +#define OPTLEN_IS(expected) do { \ + if ((expected) == 0 || optlen < (expected) || hdrlen != (expected)) \ + return false; \ + optlen -= (expected); \ + tcpc += (expected); \ +} while(0); + + while (optlen > 1) { + hdrlen = *(tcpc + 1); + switch (*tcpc) { + case 0: // EOL + /* Options processing is over. */ + return true; + case 1: // NOP + /* 1 byte, no length. All other options have a length. */ + optlen--; + tcpc++; + break; + case 2: /* MSS */ + OPTLEN_IS(4); + break; + case 3: /* Window Scale */ + OPTLEN_IS(3); + break; + case 4: /* SACK Permitted */ + OPTLEN_IS(2); + break; + case 5: /* SACK */ + if (!(hdrlen - 2) || ((hdrlen - 2) % 8)) + return false; + OPTLEN_IS(hdrlen); + break; + case 8: /* Timestamp */ + OPTLEN_IS(10); + break; + case 14: /* Alternate checksum */ + /* Sometimes used for hardware checksum offloading + * ftp://ftp.ucsd.edu/pub/csl/fastnet/faq.txt + */ + OPTLEN_IS(3); + break; + default: + OPTLEN_IS(hdrlen); + break; + } + } + + if (optlen == 1) { + // Only 1 byte left in options, this has to be NOP or EOL + return (*tcpc == 0 || *tcpc == 1); + } + // There is no way out of the previous loop that does not satisfy optlen == 0 or optlen == 1 + assert(optlen == 0); + + return true; +} + +/* Used by readip_pcap() to validate an IP packet. It checks to make sure: + * + * 1) there is enough room for an IP header in the amount of bytes read + * 2) the IP version number is correct + * 3) the IP length fields are at least as big as the standard header + * 4) the IP packet received isn't a fragment, or is the initial fragment + * 5) that next level headers seem reasonable (e.g. validateTCPhdr()) + * + * Checking the IP total length (iplen) to see if its at least as large as the + * number of bytes read (len) does not work because things like the Ethernet + * CRC also get captured and are counted in len. However, since the IP total + * length field can't be trusted, we use len instead of iplen when doing any + * further checks on lengths. readip_pcap fixes the length on it's end if we + * read more than the IP header says we should have so as to not pass garbage + * data to the caller. + */ +static bool validatepkt(const u8 *ipc, unsigned *len) { + const struct ip *ip = (struct ip *) ipc; + const void *data; + unsigned int datalen, iplen; + u8 hdr; + + if (*len < 1) { + if (o.debugging >= 3) + error("Rejecting tiny, supposed IP packet (size %u)", *len); + return false; + } + + if (ip->ip_v == 4) { + unsigned fragoff, iplen; + + datalen = *len; + data = ipv4_get_data(ip, &datalen); + if (data == NULL) { + if (o.debugging >= 3) + error("Rejecting IP packet because of invalid length"); + return false; + } + + iplen = ntohs(ip->ip_len); + + fragoff = 8 * (ntohs(ip->ip_off) & IP_OFFMASK); + if (fragoff) { + if (o.debugging >= 3) + error("Rejecting IP fragment (offset %u)", fragoff); + return false; + } + + /* OK, since the IP header has been validated, we don't want to tell + * the caller they have more packet than they really have. This can + * be caused by the Ethernet CRC trailer being counted, for example. */ + if (*len > iplen) + *len = iplen; + + hdr = ip->ip_p; + } else if (ip->ip_v == 6) { + const struct ip6_hdr *ip6 = (struct ip6_hdr *) ipc; + + datalen = *len; + data = ipv6_get_data(ip6, &datalen, &hdr); + if (data == NULL) { + if (o.debugging >= 3) + error("Rejecting IP packet because of invalid length"); + return false; + } + + iplen = ntohs(ip6->ip6_plen); + if (datalen > iplen) + *len -= datalen - iplen; + } else { + if (o.debugging >= 3) + error("Rejecting IP packet because of invalid version number %u", ip->ip_v); + return false; + } + + switch (hdr) { + case IPPROTO_TCP: + if (datalen < sizeof(struct tcp_hdr)) { + if (o.debugging >= 3) + error("Rejecting TCP packet because of incomplete header"); + return false; + } + if (!validateTCPhdr((u8 *) data, datalen)) { + if (o.debugging >= 3) + error("Rejecting TCP packet because of bad TCP header"); + return false; + } + break; + case IPPROTO_UDP: + if (datalen < sizeof(struct udp_hdr)) { + if (o.debugging >= 3) + error("Rejecting UDP packet because of incomplete header"); + return false; + } + break; + default: + break; + } + + return true; +} + +/* Read an IP packet using libpcap . We return the packet and take + a pcap descriptor and a pointer to the packet length (which we set + in the function. If you want a maximum length returned, you + should specify that in pcap_open_live() */ +/* to_usec is the timeout period in microseconds -- use 0 to skip the + test and -1 to block forever. Note that we don't interrupt pcap, so + low values (and 0) degenerate to the timeout specified + in pcap_open_live() */ +/* If rcvdtime is non-null and a packet is returned, rcvd will be + filled with the time that packet was captured from the wire by + pcap. If linknfo is not NULL, linknfo->headerlen and + linknfo->header will be filled with the appropriate values. */ +/* Specifying true for validate will enable validity checks against the + received IP packet. See validatepkt() for a list of checks. */ +const u8 *readipv4_pcap(pcap_t *pd, unsigned int *len, long to_usec, + struct timeval *rcvdtime, struct link_header *linknfo, + bool validate) { + const u8 *buf; + + buf = readip_pcap(pd, len, to_usec, rcvdtime, linknfo, validate); + if (buf != NULL) { + const struct ip *ip; + + ip = (struct ip *) buf; + if (*len < 1 || ip->ip_v != 4) + return NULL; + } + + return buf; +} + +static bool accept_any (const unsigned char *p, const struct pcap_pkthdr *h, int datalink, size_t offset) { + return true; +} + +static bool accept_ip (const unsigned char *p, const struct pcap_pkthdr *h, int datalink, size_t offset) { + const struct ip *ip = NULL; + + if (h->caplen < offset + sizeof(struct ip)) { + return false; + } + ip = (struct ip *) (p + offset); + switch (ip->ip_v) { + case 4: + case 6: + break; + default: + return false; + break; + } + + return true; +} + +const u8 *readip_pcap(pcap_t *pd, unsigned int *len, long to_usec, + struct timeval *rcvdtime, struct link_header *linknfo, bool validate) { + int datalink; + size_t offset = 0; + struct pcap_pkthdr *head; + const u8 *p; + int got_one = 0; + + if (linknfo) { + memset(linknfo, 0, sizeof(*linknfo)); + } + + if (validate) { + got_one = read_reply_pcap(pd, to_usec, accept_ip, &p, &head, rcvdtime, &datalink, &offset); + } + else { + got_one = read_reply_pcap(pd, to_usec, accept_any, &p, &head, rcvdtime, &datalink, &offset); + } + + if (!got_one) { + *len = 0; + return NULL; + } + + *len = head->caplen - offset; + p += offset; + + if (validate) { + if (!validatepkt(p, len)) { + *len = 0; + return NULL; + } + } + if (offset && linknfo) { + linknfo->datalinktype = datalink; + linknfo->headerlen = offset; + assert(offset <= MAX_LINK_HEADERSZ); + memcpy(linknfo->header, p - offset, MIN(sizeof(linknfo->header), offset)); + } + if (rcvdtime) + PacketTrace::trace(PacketTrace::RCVD, (u8 *) p, *len, + rcvdtime); + else + PacketTrace::trace(PacketTrace::RCVD, (u8 *) p, *len); + + *len = head->caplen - offset; + return p; +} + +// Returns whether the packet receive time value obtained from libpcap +// (and thus by readip_pcap()) should be considered valid. When +// invalid (Windows and Amiga), readip_pcap returns the time you called it. +bool pcap_recv_timeval_valid() { +#if defined(WIN32) || defined(__amigaos__) + return false; +#else + return true; +#endif +} + +/* Prints stats from a pcap descriptor (number of received and dropped + packets). */ +void pcap_print_stats(int logt, pcap_t *pd) { + struct pcap_stat stat; + + assert(pd != NULL); + + if (pcap_stats(pd, &stat) < 0) { + error("%s: %s", __func__, pcap_geterr(pd)); + return; + } + + log_write(logt, "pcap stats: %u packets received by filter, %u dropped by kernel.\n", stat.ps_recv, stat.ps_drop); +} + + + + +/* This function tries to determine the target's ethernet MAC address + from a received packet as follows: + 1) If linkhdr is an ethernet header, grab the src mac (otherwise give up) + 2) If overwrite is 0 and a MAC is already set for this target, give up. + 3) If the packet source address is not the target, give up. + 4) Use the routing table to try to determine rather target is + directly connected to the src host running Nmap. If it is, set the MAC. + + This function returns 0 if it ends up setting the MAC, nonzero otherwise. */ +int setTargetMACIfAvailable(Target *target, struct link_header *linkhdr, + const struct sockaddr_storage *src, int overwrite) { + if (!linkhdr || !target || !src) + return 1; + + if (linkhdr->datalinktype != DLT_EN10MB || linkhdr->headerlen != 14) + return 2; + + if (!overwrite && target->MACAddress()) + return 3; + + if (sockaddr_storage_cmp(src, target->TargetSockAddr()) != 0) + return 4; + + /* Sometimes bogus MAC address still gets through, like during some localhost scans */ + if (memcmp(linkhdr->header + 6, "\0\0\0\0\0\0", 6) == 0) + return 5; + + if (target->ifType() == devt_ethernet && target->directlyConnected()) { + /* Yay! This MAC address seems valid */ + target->setMACAddress(linkhdr->header + 6); + return 0; + } + + return 5; +} + + +/* This function ensures that the next hop MAC address for a target is + filled in. This address is the target's own MAC if it is directly + connected, and the next hop mac otherwise. Returns true if the + address is set when the function ends, false if not. This function + firt checks if it is already set, if not it tries the arp cache, + and if that fails it sends an ARP request itself. This should be + called after an ARP scan if many directly connected machines are + involved. setDirectlyConnected() (whether true or false) should + have already been called on target before this. The target device + and src mac address should also already be set. */ +bool setTargetNextHopMAC(Target *target) { + struct sockaddr_storage targetss; + size_t sslen; + u8 mac[6]; + + if (target->ifType() != devt_ethernet) + return false; /* Duh. */ + + /* First check if we already have it, duh. */ + if (target->NextHopMACAddress()) + return true; + + /* For connected machines, it is the same as the target addy */ + if (target->directlyConnected() && target->MACAddress()) { + target->setNextHopMACAddress(target->MACAddress()); + return true; + } + + if (target->directlyConnected()) { + target->TargetSockAddr(&targetss, &sslen); + } else { + if (!target->nextHop(&targetss, &sslen)) + fatal("%s: Failed to determine nextHop to target", __func__); + } + + if (getNextHopMAC(target->deviceFullName(), target->SrcMACAddress(), target->SourceSockAddr(), &targetss, mac)) { + target->setNextHopMACAddress(mac); + return true; + } + + /* I'm afraid that we couldn't find it! Maybe it doesn't exist? */ + return false; +} + +/* Like to getTargetNextHopMAC(), but for arbitrary hosts (not Targets) */ +bool getNextHopMAC(const char *iface, const u8 *srcmac, const struct sockaddr_storage *srcss, + const struct sockaddr_storage *dstss, u8 *dstmac) { + arp_t *a; + struct arp_entry ae; + + /* First, let us check the Nmap arp cache ... */ + if (mac_cache_get(dstss, dstmac)) + return true; + + /* Maybe the system ARP cache will be more helpful */ + a = arp_open(); + addr_ston((sockaddr *) dstss, &ae.arp_pa); + if (arp_get(a, &ae) == 0) { + mac_cache_set(dstss, ae.arp_ha.addr_eth.data); + memcpy(dstmac, ae.arp_ha.addr_eth.data, 6); + arp_close(a); + return true; + } + arp_close(a); + + /* OK, the last choice is to send our own damn ARP request (and + retransmissions if necessary) to determine the MAC */ + if (dstss->ss_family == AF_INET) { + if (doArp(iface, srcmac, srcss, dstss, dstmac, PacketTrace::traceArp)) { + mac_cache_set(dstss, dstmac); + return true; + } + } else if (dstss->ss_family == AF_INET6) { + if (doND(iface, srcmac, srcss, dstss, dstmac, PacketTrace::traceND)) { + mac_cache_set(dstss, dstmac); + return true; + } + } + + return false; +} + + +int nmap_route_dst(const struct sockaddr_storage *dst, struct route_nfo *rnfo) { + struct sockaddr_storage spoofss; + size_t spoofsslen; + + if (o.spoofsource) { + o.SourceSockAddr(&spoofss, &spoofsslen); + return route_dst(dst, rnfo, o.device, &spoofss); + } else { + return route_dst(dst, rnfo, o.device, NULL); + } +} + + +/* Maximize the receive buffer of a socket descriptor (up to 500K) */ +void max_rcvbuf(int sd) { + int optval = 524288; /* 2^19 */ + recvfrom6_t optlen = sizeof(int); + +#ifndef WIN32 + if (setsockopt (sd, SOL_SOCKET, SO_RCVBUF, (const char *) &optval, optlen)) + if (o.debugging) + perror("Problem setting large socket receive buffer"); + if (o.debugging) { + getsockopt(sd, SOL_SOCKET, SO_RCVBUF, (char *) &optval, &optlen); + log_write(LOG_STDOUT, "Our buffer size is now %d\n", optval); + } +#endif /* WIN32 */ +} + + +/* Do a receive (recv()) on a socket and stick the results (up to + len) into buf . Give up after 'seconds'. Returns the number of + bytes read (or -1 in the case of an error. It only does one recv + (it will not keep going until len bytes are read). If timedout is + not NULL, it will be set to zero (no timeout occurred) or 1 (it + did). */ +int recvtime(int sd, char *buf, int len, int seconds, int *timedout) { + + int res; + struct timeval timeout; + fd_set readfd; + + timeout.tv_sec = seconds; + timeout.tv_usec = 0; + FD_ZERO(&readfd); + checked_fd_set(sd, &readfd); + if (timedout) + *timedout = 0; + res = select(sd + 1, &readfd, NULL, NULL, &timeout); + if (res > 0) { + res = recv(sd, buf, len, 0); + if (res >= 0) + return res; + gh_perror("recv in %s", __func__); + return 0; + } else if (!res) { + if (timedout) + *timedout = 1; + return 0; + } + gh_perror("select() in %s", __func__); + return -1; +} + +/* Examines the given tcp packet and obtains the TCP timestamp option + information if available. Note that the CALLER must ensure that + "tcp" contains a valid header (in particular the th_off must be the + true packet length and tcp must contain it). If a valid timestamp + option is found in the header, nonzero is returned and the + 'timestamp' and 'echots' parameters are filled in with the + appropriate value (if non-null). Otherwise 0 is returned and the + parameters (if non-null) are filled with 0. Remember that the + correct way to check for errors is to look at the return value + since a zero ts or echots could possibly be valid. */ +int gettcpopt_ts(const struct tcp_hdr *tcp, u32 *timestamp, u32 *echots) { + + unsigned char *p; + int len = 0; + int op; + int oplen; + + /* first we find where the tcp options start ... */ + p = ((unsigned char *) tcp) + 20; + len = 4 * tcp->th_off - 20; + while (len > 0 && *p != 0 /* TCPOPT_EOL */ ) { + op = *p++; + if (op == 0 /* TCPOPT_EOL */ ) + break; + if (op == 1 /* TCPOPT_NOP */ ) { + len--; + continue; + } + oplen = *p++; + if (oplen < 2) + break; /* No infinite loops, please */ + if (oplen > len) + break; /* Not enough space */ + if (op == 8 /* TCPOPT_TIMESTAMP */ && oplen == 10) { + /* Legitimate ts option */ + if (timestamp) { + memcpy((char *) timestamp, p, 4); + *timestamp = ntohl(*timestamp); + } + p += 4; + if (echots) { + memcpy((char *) echots, p, 4); + *echots = ntohl(*echots); + } + return 1; + } + len -= oplen; + p += oplen - 2; + } + + /* Didn't find anything */ + if (timestamp) + *timestamp = 0; + if (echots) + *echots = 0; + return 0; +} |