/*************************************************************************** * netutil.cc * * * ***********************IMPORTANT NMAP LICENSE TERMS************************ * * * The Nmap Security Scanner is (C) 1996-2011 Insecure.Com LLC. Nmap is * * also a registered trademark of Insecure.Com LLC. This program is free * * software; you may redistribute and/or modify it under the terms of the * * GNU General Public License as published by the Free Software * * Foundation; Version 2 with the clarifications and exceptions described * * below. This guarantees your right to use, modify, and redistribute * * this software under certain conditions. If you wish to embed Nmap * * technology into proprietary software, we sell alternative licenses * * (contact sales@insecure.com). Dozens of software vendors already * * license Nmap technology such as host discovery, port scanning, OS * * detection, and version detection. * * * * Note that the GPL places important restrictions on "derived works", yet * * it does not provide a detailed definition of that term. To avoid * * misunderstandings, we consider an application to constitute a * * "derivative work" for the purpose of this license if it does any of the * * following: * * o Integrates source code from Nmap * * o Reads or includes Nmap copyrighted data files, such as * * nmap-os-db or nmap-service-probes. * * o Executes Nmap and parses the results (as opposed to typical shell or * * execution-menu apps, which simply display raw Nmap output and so are * * not derivative works.) * * o Integrates/includes/aggregates Nmap into a proprietary executable * * installer, such as those produced by InstallShield. * * o Links to a library or executes a program that does any of the above * * * * The term "Nmap" should be taken to also include any portions or derived * * works of Nmap. This list is not exclusive, but is meant to clarify our * * interpretation of derived works with some common examples. Our * * interpretation applies only to Nmap--we don't speak for other people's * * GPL works. * * * * If you have any questions about the GPL licensing restrictions on using * * Nmap in non-GPL works, we would be happy to help. As mentioned above, * * we also offer alternative license to integrate Nmap into proprietary * * applications and appliances. These contracts have been sold to dozens * * of software vendors, and generally include a perpetual license as well * * as providing for priority support and updates as well as helping to * * fund the continued development of Nmap technology. Please email * * sales@insecure.com for further information. * * * * As a special exception to the GPL terms, Insecure.Com LLC grants * * permission to link the code of this program with any version of the * * OpenSSL library which is distributed under a license identical to that * * listed in the included docs/licenses/OpenSSL.txt file, and distribute * * linked combinations including the two. You must obey the GNU GPL in all * * respects for all of the code used other than OpenSSL. If you modify * * this file, you may extend this exception to your version of the file, * * but you are not obligated to do so. * * * * If you received these files with a written license agreement or * * contract stating terms other than the terms above, then that * * alternative license agreement takes precedence over these comments. * * * * 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 (none * * have been found so far). * * * * Source code also allows you to port Nmap to new platforms, fix bugs, * * and add new features. You are highly encouraged to send your changes * * to nmap-dev@insecure.org for possible incorporation into the main * * distribution. By sending these changes to Fyodor or one of the * * Insecure.Org development mailing lists, it is assumed that you are * * offering the Nmap Project (Insecure.Com LLC) the unlimited, * * non-exclusive right to reuse, modify, and relicense the code. Nmap * * will always be available Open Source, but this is important because the * * inability to relicense code has caused devastating problems for other * * Free Software projects (such as KDE and NASM). We also occasionally * * relicense the code to third parties as discussed above. If you wish to * * specify special license conditions of your contributions, just say so * * when you send them. * * * * This program 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. See the GNU * * General Public License v2.0 for more details at * * http://www.gnu.org/licenses/gpl-2.0.html , or in the COPYING file * * included with Nmap. * * * ***************************************************************************/ #if HAVE_CONFIG_H #include "../nmap_config.h" #endif #include "nbase.h" #ifdef WIN32 #include "mswin32/winclude.h" #include "pcap-int.h" #else #include #endif #include #include #include #if HAVE_SYS_SOCKET_H #include #endif #if HAVE_SYS_SOCKIO_H #include /* SIOCGIFCONF for Solaris */ #endif #include #if HAVE_NETINET_IN_H #include #endif #ifndef NETINET_IN_SYSTM_H /* This guarding is needed for at least some versions of OpenBSD */ #include #define NETINET_IN_SYSTM_H #endif #if HAVE_NET_IF_H #ifndef NET_IF_H /* This guarding is needed for at least some versions of OpenBSD */ #include #define NET_IF_H #endif #endif #ifndef NETINET_IP_H /* This guarding is needed for at least some versions of OpenBSD */ #include #define NETINET_IP_H #endif #if HAVE_SYS_RESOURCE_H #include #endif #include "netutil.h" #define NBASE_MAX_ERR_STR_LEN 1024 /* Max length of an error message */ /** 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() */ int 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); return 0; } /* 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; for(;*c;c++){ switch(s){ case SLASH: // parse \x00 string if(*c == 'x'){// just ignore this char base = 16; break; } if(isxdigit(*c)){ *d++ = strtol(c, &n, base); 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='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= 0 && rc < sizeof(portbuf)); rc = getaddrinfo(hostname, portbuf, &hints, &result); if (rc != 0 || result == NULL) return 0; 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 1; } /* * 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 * * for the most recent assigments and * 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 6: /* USA Army ISC */ case 7: /* used for BGP protocol */ case 10: /* the infamous 10.0.0.0/8 */ case 39: /* 039/8 is IANA reserved */ case 55: /* misc. U.S.A. Armed forces */ case 106: /* 106/8 is IANA reserved */ case 127: /* 127/8 is reserved for loopback */ case 179: /* 179/8 is IANA reserved */ case 185: /* 185/8 is IANA reserved */ return 1; default: break; } /* 102-104/8 is IANA reserved */ if (i1 >= 102 && i1 <= 104) return 1; /* 172.16.0.0/12 is reserved for private nets by RFC1819 */ 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 RFC1819 */ 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 */ 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 ARPCACHE_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 ARPCACHE_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. 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. */ #define ARPCACHE_GET 1 #define ARPCACHE_SET 2 static int do_arp_cache(int command, struct sockaddr_storage *ss, u8 *mac) { struct sockaddr_in *sin = (struct sockaddr_in *) ss; struct ArpCache { u32 ip; /* Network byte order */ u8 mac[6]; }; static struct ArpCache *Cache = NULL; static int ArpCapacity = 0; static int ArpCacheSz = 0; int i; if (sin->sin_family != AF_INET) return 0; if (command == ARPCACHE_GET) { for (i = 0; i < ArpCacheSz; i++) { if (Cache[i].ip == sin->sin_addr.s_addr) { memcpy(mac, Cache[i].mac, 6); return 1; } } return 0; } assert(command == ARPCACHE_SET); if (ArpCacheSz == ArpCapacity) { if (ArpCapacity == 0) ArpCapacity = 32; else ArpCapacity <<= 2; Cache = (struct ArpCache *) safe_realloc(Cache, ArpCapacity * sizeof(struct ArpCache)); } /* Ensure that it isn't already there ... */ for (i = 0; i < ArpCacheSz; i++) { if (Cache[i].ip == sin->sin_addr.s_addr) { memcpy(Cache[i].mac, mac, 6); return 1; } } /* Add it to the end of the list */ Cache[i].ip = sin->sin_addr.s_addr; memcpy(Cache[i].mac, mac, 6); ArpCacheSz++; return 1; } /* A couple of trivial functions that maintain a cache of IP to MAC * Address entries. Function arp_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 arp_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. arp_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 arp_cache_get(struct sockaddr_storage *ss, u8 *mac){ return do_arp_cache(ARPCACHE_GET, ss, mac); } int arp_cache_set(struct sockaddr_storage *ss, u8 *mac){ return do_arp_cache(ARPCACHE_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; } /* For computing TCP/UDP checksums, see RFC 1071 and TCP/IP Illustrated sections 3.2, 11.3, and 17.3. */ unsigned short tcpudp_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. */ return ip_cksum_carry(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 } /* Returns whether the system supports pcap_get_selectable_fd() properly */ int pcap_selectable_fd_valid() { #if defined(WIN32) || defined(MACOSX) || (defined(FREEBSD) && (__FreeBSD_version < 500000)) return 0; #else return 1; #endif } /* 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) { #if defined(WIN32) || defined(MACOSX) || (defined(FREEBSD) && (__FreeBSD_version < 500000)) return -1; #else assert(pcap_selectable_fd_valid()); return pcap_get_selectable_fd(p); #endif } /* 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 fd, ret; fd_set rfds; if ((fd = my_pcap_get_selectable_fd(p)) == -1) return -1; FD_ZERO(&rfds); 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); 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 "" */ 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 %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, ">"); } /* A trivial function used with qsort to sort the routes by netmask */ static int nmaskcmp(const void *a, const void *b) { struct sys_route *r1 = (struct sys_route *) a; struct sys_route *r2 = (struct sys_route *) b; if (r1->netmask == r2->netmask) { /* 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; } if (ntohl(r1->netmask) > ntohl(r2->netmask)) return -1; else return 1; } #if WIN32 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; int num_aliases_done; 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 (entry->intf_addr.addr_type == ADDR_TYPE_IP) { addr_ntos(&entry->intf_addr, (struct sockaddr *) &dcrn->ifaces[dcrn->numifaces].addr); dcrn->ifaces[dcrn->numifaces].netmask_bits = entry->intf_addr.addr_bits; } primary_done = true; } else if (num_aliases_done < (int) entry->intf_alias_num) { if (entry->intf_alias_addrs[num_aliases_done].addr_type == ADDR_TYPE_IP) { addr_ntos(&entry->intf_alias_addrs[num_aliases_done], (struct sockaddr *) &dcrn->ifaces[dcrn->numifaces].addr); 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) { 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].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; } #else /* !WIN32 */ /* Get a list of interfaces using ioctl(SIOCGIFCONF). */ static struct interface_info *getinterfaces_siocgifconf(int *howmany, char *errstr, size_t errstrlen) { struct interface_info *devs; int count = 0; int capacity = 0; struct ifconf ifc; struct ifreq *ifr; int sd; int len; assert(howmany); capacity = 16; devs = (struct interface_info *) safe_zalloc(sizeof(struct interface_info) * capacity); /* Dummy socket for ioctl */ sd = socket(AF_INET, SOCK_DGRAM, 0); if (sd < 0){ if(errstr) Snprintf(errstr, errstrlen, "socket in %s", __func__); *howmany=-1; return NULL; } ifc.ifc_len = 20480; ifc.ifc_buf = (char *) safe_zalloc(ifc.ifc_len); /* Returns an array of struct ifreq in ifc.ifc_req, which is a union with ifc.ifc_buf. */ if (ioctl(sd, SIOCGIFCONF, &ifc) < 0){ if(errstr) Snprintf(errstr, errstrlen, "Failed to determine your configured interfaces!\n"); *howmany=-1; return NULL; } if (ifc.ifc_len == 0){ if(errstr) Snprintf(errstr, errstrlen, "%s: SIOCGIFCONF claims you have no network interfaces!\n", __func__); *howmany=-1; return NULL; } ifr = ifc.ifc_req; for (ifr = ifc.ifc_req; ifr && ifr->ifr_name[0] && (void *) ifr < (void *)((char *) ifc.ifc_buf + ifc.ifc_len); ifr = (struct ifreq *) ((char *) ifr + len)) { struct sockaddr_in *sin; struct ifreq tmpifr; u16 ifflags; int rc; char *p; len = sizeof(struct ifreq); #if HAVE_SOCKADDR_SA_LEN /* Some platforms (such as FreeBSD) have an sa_len member that may make the ifr longer than sizeof(struct ifreq). */ if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru)) len += ifr->ifr_addr.sa_len - sizeof(ifr->ifr_ifru); #endif /* skip any device with no name */ if (ifr->ifr_name[0] == '\0') continue; /* We currently only handle IPv4 */ sin = (struct sockaddr_in *) &ifr->ifr_addr; if (sin->sin_family != AF_INET) continue; /* Make room for this new interface if necessary. */ if (count >= capacity) { capacity <<= 2; devs = (struct interface_info *) safe_realloc(devs, sizeof(struct interface_info) * capacity); } /* We know the address, put it in the array. */ memcpy(&(devs[count].addr), sin, MIN(sizeof(devs[count].addr), sizeof(*sin))); Strncpy(devs[count].devname, ifr->ifr_name, sizeof(devs[count].devname)); Strncpy(devs[count].devfullname, ifr->ifr_name, sizeof(devs[count].devfullname)); /* devname isn't allowed to have alias qualification */ p = strchr(devs[count].devname, ':'); if (p != NULL) *p = '\0'; /* Use tmpifr for further ioctl requests. We're going to make a bunch of ioctl calls to learn about the interface and set fields in devs[count]. The Linux netdevice(7) man page says that you only have to set ifr_name before making the ioctl, but perhaps other platforms need ifr_addr to be set too. ifr_name will persist but ifr_addr is in a union with the ioctl return value, so it has to be reset before every call. The general pattern is memcpy, then ioctl. */ Strncpy(tmpifr.ifr_name, ifr->ifr_name, sizeof(tmpifr.ifr_name)); /* Look up the netmask. Note setting of ifr_addr. */ memcpy(&tmpifr.ifr_addr, sin, MIN(sizeof(tmpifr.ifr_addr), sizeof(*sin))); rc = ioctl(sd, SIOCGIFNETMASK, &tmpifr); if (rc < 0 && errno != EADDRNOTAVAIL){ if(errstr) Snprintf(errstr, errstrlen, "Failed to determine the netmask of %s!", tmpifr.ifr_name); *howmany=-1; return NULL; } else if (rc < 0) devs[count].netmask_bits = 32; else { /* We would use ifr_netmask, but that's only on Linux, so use ifr_addr which shares the same memory space in a union. */ addr_stob(&(tmpifr.ifr_addr), &devs[count].netmask_bits); } /* Now we need to determine the device type ... this technique is kinda iffy ... may not be portable. */ /* Get the flags. */ memcpy(&tmpifr.ifr_addr, sin, MIN(sizeof(tmpifr.ifr_addr), sizeof(*sin))); rc = ioctl(sd, SIOCGIFFLAGS, &tmpifr); if (rc < 0){ if(errstr) Snprintf(errstr, errstrlen, "Failed to get IF Flags for device %s", ifr->ifr_name); *howmany=-1; return NULL; } ifflags = tmpifr.ifr_flags; if (ifflags & IFF_LOOPBACK) { devs[count].device_type = devt_loopback; } else if (ifflags & IFF_BROADCAST) { devs[count].device_type = devt_ethernet; /* If the device type is ethernet, get the MAC address. */ #ifdef SIOCGIFHWADDR memcpy(&tmpifr.ifr_addr, sin, MIN(sizeof(tmpifr.ifr_addr), sizeof(*sin))); rc = ioctl(sd, SIOCGIFHWADDR, &tmpifr); if (rc < 0 && errno != EADDRNOTAVAIL){ if(errstr) Snprintf(errstr, errstrlen, "Failed to determine the MAC address of %s!", tmpifr.ifr_name); *howmany=-1; return NULL; } else if (rc >= 0) memcpy(devs[count].mac, &tmpifr.ifr_addr.sa_data, 6); #else /* Let's just let libdnet handle it ... */ eth_t *ethsd = eth_open_cached(devs[count].devname); eth_addr_t ethaddr; if (!ethsd) { netutil_error("Warning: Unable to open interface %s -- skipping it.", devs[count].devname); continue; } if (eth_get(ethsd, ðaddr) != 0) { netutil_error("Warning: Unable to get hardware address for interface %s -- skipping it.", devs[count].devname); continue; } memcpy(devs[count].mac, ethaddr.data, 6); #endif /*SIOCGIFHWADDR*/ } else if (ifflags & IFF_POINTOPOINT) { devs[count].device_type = devt_p2p; } else { devs[count].device_type = devt_other; } if (ifflags & IFF_UP) devs[count].device_up = 1; else devs[count].device_up = 0; #ifdef SIOCGIFMTU memcpy(&tmpifr.ifr_addr, sin, MIN(sizeof(tmpifr.ifr_addr), sizeof(*sin))); rc = ioctl(sd, SIOCGIFMTU, &tmpifr); if (rc < 0) { if(errstr) Snprintf(errstr, errstrlen, "Failed to determine the mtu of %s!", tmpifr.ifr_name); *howmany=-1; return NULL; } else { #ifdef ifr_mtu devs[count].mtu = tmpifr.ifr_mtu; #else /* Some systems lack ifr_mtu and a common solution (see pcap, dnet and * others) is using ifr_metric instead */ devs[count].mtu = tmpifr.ifr_metric; #endif } #else devs[count].mtu = 0; #endif /* All done with this interface. Increase the count. */ count++; } free(ifc.ifc_buf); close(sd); *howmany = count; return devs; } #endif /* 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) { static int initialized = 0; static struct interface_info *mydevs; static int numifaces = 0; if (!initialized) { #if WIN32 /* On Win32 we just use Dnet to determine the interface list */ mydevs = getinterfaces_dnet(&numifaces, errstr, errstrlen); #else mydevs = getinterfaces_siocgifconf(&numifaces, errstr, errstrlen); #endif initialized = 1; } /* These will propagate any error produced in getinterfaces_xxxx() to * the caller. */ if (howmany) *howmany = numifaces; return mydevs; } /* The 'dev' passed in must be at least 32 bytes long. Returns 0 on success. */ int ipaddr2devname(char *dev, const struct in_addr *addr) { struct interface_info *ifaces; struct sockaddr_in *sin; int numifaces; int i; ifaces = getinterfaces(&numifaces, NULL, 0); if (ifaces == NULL) return -1; for (i = 0; i < numifaces; i++) { sin = (struct sockaddr_in *) &ifaces[i].addr; if (sin->sin_family != AF_INET) continue; if (addr->s_addr == sin->sin_addr.s_addr) { Strncpy(dev, ifaces[i].devname, 32); return 0; } } return -1; } int devname2ipaddr(char *dev, struct in_addr *addr) { struct interface_info *mydevs; struct sockaddr_in *s; int numdevs; int i; mydevs = getinterfaces(&numdevs, NULL, 0); if (!mydevs) return -1; for (i = 0; i < numdevs; i++) { s = (struct sockaddr_in *) &mydevs[i].addr; if (s->sin_family != AF_INET) /* Currently we only support IPv4 */ continue; if (!strcmp(dev, mydevs[i].devfullname)) { memcpy(addr, (char *) &s->sin_addr, sizeof(struct in_addr)); return 0; } } return -1; } /* Looks for an interface with the given name (iname), 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) { 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) return &ifaces[ifnum]; } return NULL; } /* Read system routes from a handle to a /proc/net/route file. */ static struct sys_route *getsysroutes_proc(FILE *routefp, int *howmany, char *errstr, size_t errstrlen) { struct sys_route *routes = NULL; int route_capacity = 128; struct interface_info *ifaces; char buf[1024]; char iface[16]; char *p, *endptr; struct interface_info *ii; u32 routeaddr, mask; struct sockaddr_in *sin; int numifaces = 0, numroutes = 0; int i; assert(howmany); /* Obtain list of system network interfaces */ if( (ifaces=getinterfaces(&numifaces, errstr, errstrlen)) == NULL ){ *howmany=-1; return NULL; } routes = (struct sys_route *) safe_zalloc(route_capacity * sizeof(struct sys_route)); /* Kill the first line (column headers) */ errno = 0; if (fgets(buf, sizeof(buf), routefp) == NULL) { if (errno){ if(errstr) Snprintf(errstr, errstrlen, "Read error in /proc/net/route"); }else{ if(errstr) Snprintf(errstr, errstrlen, "Premature EOF in /proc/net/route"); } *howmany=-1; return NULL; } while (fgets(buf, sizeof(buf), routefp)) { p = strtok(buf, " \t\n"); if (!p) { netutil_error("Could not find interface in /proc/net/route line"); continue; } if (*p == '*') continue; /* Deleted route -- any other valid reason for a route to start with an asterict? */ Strncpy(iface, p, sizeof(iface)); p = strtok(NULL, " \t\n"); if (!p) { netutil_error("Could not find destination in /proc/net/route line"); continue; } endptr = NULL; routes[numroutes].dest = strtoul(p, &endptr, 16); if (!endptr || *endptr) { netutil_error("Failed to determine Destination from /proc/net/route"); continue; } /* Now for the gateway */ p = strtok(NULL, " \t\n"); if (!p) { netutil_error("Could not find gateway in /proc/net/route line"); continue; } endptr = NULL; routes[numroutes].gw.s_addr = strtoul(p, &endptr, 16); if (!endptr || *endptr) { netutil_error("Failed to determine gw for %s from /proc/net/route", iface); } for (i = 0; i < 5; i++) { p = strtok(NULL, " \t\n"); if (!p) break; } if (!p) { netutil_error("Failed to find field %d in /proc/net/route", i + 2); continue; } endptr = NULL; routes[numroutes].netmask = strtoul(p, &endptr, 16); if (!endptr || *endptr) { netutil_error("Failed to determine mask from /proc/net/route"); continue; } for (i = 0; i < numifaces; i++) { if (!strcmp(iface, ifaces[i].devfullname)) { routes[numroutes].device = &ifaces[i]; break; } } /* If device name in the route file does not match the full name (including alias extension) of any interface, then try to find at least an alias of the proper interface. */ if (i == numifaces) { for (i = 0; i < numifaces; i++) { if (!strcmp(iface, ifaces[i].devname)) { routes[numroutes].device = &ifaces[i]; break; } } } if (i == numifaces) { netutil_error("Failed to find device %s which was referenced in /proc/net/route", iface); continue; } /* Now to deal with some alias nonsense ... at least on Linux this file will just list the short name, even though IP information (such as source address) from an alias must be used. So if the purported device can't reach the gateway (or destination address for directly connected routes), try to find a device that starts with the same short devname, but can (e.g. eth0 -> eth0:3) */ if (routes[numroutes].gw.s_addr != 0) routeaddr = routes[numroutes].gw.s_addr; else routeaddr = routes[numroutes].dest; ii = &ifaces[i]; mask = htonl((unsigned long) (0 - 1) << (32 - ii->netmask_bits)); sin = (struct sockaddr_in *) &ii->addr; if ((sin->sin_addr.s_addr & mask) != (routeaddr & mask)) { for (i = 0; i < numifaces; i++) { if (ii == &ifaces[i]) continue; if (strcmp(ii->devname, ifaces[i].devname) == 0) { sin = (struct sockaddr_in *) &ifaces[i].addr; if ((sin->sin_addr.s_addr & mask) == (routeaddr & mask)) { routes[numroutes].device = &ifaces[i]; } } } } numroutes++; if (numroutes >= route_capacity) { route_capacity <<= 2; routes = (struct sys_route *) safe_realloc(routes, route_capacity * sizeof(struct sys_route)); } } *howmany = numroutes; return routes; } /* 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; u32 mask; struct sockaddr_in *sin; 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++) { /* First we match up routes whose gateway address directly matches the address of an interface. */ for (j = 0; j < numifaces; j++) { sin = (struct sockaddr_in *) &ifaces[j].addr; mask = htonl((unsigned long) (0 - 1) << (32 - ifaces[j].netmask_bits)); if ((sin->sin_addr.s_addr & mask) == (dcrn->routes[i].gw.s_addr & mask)) { 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 (dcrn->routes[i].gw.s_addr == 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) { netutil_error("WARNING: Unable to find appropriate interface for system route to %s", inet_ntoa(dcrn->routes[i].gw)); /* 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 that it is the proper type of route ... */ if (entry->route_dst.addr_type != ADDR_TYPE_IP || entry->route_gw.addr_type != ADDR_TYPE_IP) return 0; /* Not interested in IPv6 routes at the moment ... */ /* 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 */ dcrn->routes[dcrn->numroutes].dest = entry->route_dst.addr_ip; addr_btom(entry->route_dst.addr_bits, &dcrn->routes[dcrn->numroutes].netmask, sizeof(dcrn->routes[dcrn->numroutes].netmask)); dcrn->routes[dcrn->numroutes].gw.s_addr = entry->route_gw.addr_ip; 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; FILE *routefp; assert(howmany); if (routes != NULL) { /* We have it cached. */ *howmany = numroutes; return routes; } /* First let us try Linux-style /proc/net/route */ routefp = fopen("/proc/net/route", "r"); if (routefp) { routes = getsysroutes_proc(routefp, howmany, errstr, errstrlen); fclose(routefp); } else { 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 */ qsort(routes, numroutes, sizeof(routes[0]), nmaskcmp); } 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 in_addr *const addr) { char dev[128]; /* If it is 0.0.0.0 or starts with 127 then it is probably localhost */ if ((addr->s_addr & htonl(0xFF000000)) == htonl(0x7F000000)) return 1; if (!addr->s_addr) return 1; /* If it is the same addy as a local interface, then it is probably localhost */ if (ipaddr2devname(dev, addr) != -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. * Returns 1 if the address is private or 0 otherwise. */ int isipprivate(const struct in_addr *const addr) { char *ipc; unsigned char i1, i2; if (!addr) return 0; ipc = (char *) &(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; /* 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); switch(nextheader){ case 0: if(acronym) strncpy(buffer, "HOPOPT", 128); else strncpy(buffer, "IPv6 Hop-by-Hop Option", 128); break; case 1: if(acronym) strncpy(buffer, "ICMP", 128); else strncpy(buffer, "Internet Control Message", 128); break; case 2: if(acronym) strncpy(buffer, "IGMP", 128); else strncpy(buffer, "Internet Group Management", 128); break; case 4: if(acronym) strncpy(buffer, "IP", 128); else strncpy(buffer, "IP in IP (encapsulation)", 128); break; case 6: if(acronym) strncpy(buffer, "TCP", 128); else strncpy(buffer, "Transmission Control Protocol", 128); break; case 8: if(acronym) strncpy(buffer, "EGP", 128); else strncpy(buffer, "Exterior Gateway Protocol", 128); break; case 9: if(acronym) strncpy(buffer, "IGP", 128); else strncpy(buffer, "Interior Gateway Protocol", 128); break; case 17: if(acronym) strncpy(buffer, "UDP", 128); else strncpy(buffer, "User Datagram", 128); break; case 41: if(acronym) strncpy(buffer, "IPv6", 128); else strncpy(buffer, "Internet Protocol version 6", 128); break; case 43: if(acronym) strncpy(buffer, "IPv6-Route", 128); else strncpy(buffer, "Routing Header for IPv6", 128); break; case 44: if(acronym) strncpy(buffer, "IPv6-Frag", 128); else strncpy(buffer, "Fragment Header for IPv6", 128); break; case 50: if(acronym) strncpy(buffer, "ESP", 128); else strncpy(buffer, "Encap Security Payload", 128); break; case 51: if(acronym) strncpy(buffer, "AH", 128); else strncpy(buffer, "Authentication Header", 128); break; case 55: if(acronym) strncpy(buffer, "MOBILE", 128); else strncpy(buffer, "IP Mobility", 128); break; case 58: if(acronym) strncpy(buffer, "IPv6-ICMP", 128); else strncpy(buffer, "ICMP for IPv6", 128); break; case 59: if(acronym) strncpy(buffer, "IPv6-NoNxt", 128); else strncpy(buffer, "No Next Header for IPv6", 128); break; case 60: if(acronym) strncpy(buffer, "IPv6-Opts", 128); else strncpy(buffer, "Destination Options for IPv6", 128); break; case 70: if(acronym) strncpy(buffer, "VISA", 128); else strncpy(buffer, "VISA Protocol", 128); break; case 88: if(acronym) strncpy(buffer, "EIGRP", 128); else strncpy(buffer, "Enhanced Interior Gateway Routing Protocol ", 128); break; case 94: if(acronym) strncpy(buffer, "IPIP", 128); else strncpy(buffer, "IP-within-IP Encapsulation Protocol", 128); break; case 132: if(acronym) strncpy(buffer, "SCTP", 128); else strncpy(buffer, "Stream Control Transmission Protocol", 128); break; case 133: if(acronym) strncpy(buffer, "FC", 128); else strncpy(buffer, "Fibre Channel", 128); break; case 135: if(acronym) strncpy(buffer, "MH", 128); else strncpy(buffer, "Mobility Header", 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(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 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) // 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 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 (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=%i", 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 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 . */ const char *ippackethdrinfo(const u8 *packet, u32 len, int detail) { struct ip *ip = (struct ip *) packet; /* IPv4 header structure. */ 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. */ struct in_addr saddr, daddr; /* Src and Dst IPs in binary. */ 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; /* Ture if IP DF flag is set. */ int reserved_flag = 0; /* True if IP Reserved flag is set. */ size_t iphdrlen=0; /* Length of the IP (4 or 6) header */ u8 nextproto=0; /* Protocol after IP (4 or 6) header */ /* Ensure IP version makes sense */ if (ip->ip_v != 4 && ip->ip_v != 6 ) return "BOGUS! IP Version in packet is not 4"; /* 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( ip->ip_v == 4 ){ /* IPv4 */ if (len < 20) return "BOGUS! Packet too short."; else{ iphdrlen=ip->ip_hl * 4; nextproto=ip->ip_p; } /* Obtain IP source and destination info */ saddr.s_addr = ip->ip_src.s_addr; daddr.s_addr = ip->ip_dst.s_addr; inet_ntop(AF_INET, &saddr, srchost, sizeof(srchost)); inet_ntop(AF_INET, &daddr, 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=%d iplen=%d%s %s%s%s", ip->ip_ttl, ntohs(ip->ip_id), 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=%d proto=%d csum=0x%04x iplen=%d%s %s%s%s", ip->ip_ttl, ntohs(ip->ip_id), ip->ip_p, ntohs(ip->ip_sum), 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=%d id=%d%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, ntohs(ip->ip_len), 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 */ /* I'd rather use a regular u8 pointer to access the IPv6 header because * it's surprinsingly easy to fuck the whole thing up using structures due * to the weird IPv6 field alignment and the f*** compiler padding structs * when it shouldn't. */ u8 *ipv6pnt = (u8 *)packet; if (len < 40) return "BOGUS! IPv6 Packet too short."; else{ iphdrlen=40; nextproto=ipv6pnt[6]; } /* Obtain IP source and destination info */ struct in6_addr ip6_src; struct in6_addr ip6_dst; memcpy(ip6_src.s6_addr, &ipv6pnt[8], 16); memcpy(ip6_dst.s6_addr, &ipv6pnt[24], 16); inet_ntop(AF_INET6, &ip6_src, srchost, sizeof(srchost)); inet_ntop(AF_INET6, &ip6_dst, dsthost, sizeof(dsthost)); /* Obtain payload length, next protocol and hop limit */ u16 *ipv6_pl = (u16 *)(&ipv6pnt[4]); u8 *ipv6_nh = (u8 *)(& ipv6pnt[6]); u8 *ipv6_hl = (u8 *)(& ipv6pnt[7]); /* Obtain flow label and traffic class */ u32 *word = (u32 *)(&ipv6pnt[0]); u32 flow= ntohl( *word ); 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=%d", (*ipv6_hl), ip6_fl, ntohs(*ipv6_pl) ); }else if( detail == MEDIUM_DETAIL ){ Snprintf(ipinfo, sizeof(ipinfo), "hopl=%d tclass=%d flow=%x payloadlen=%d", (*ipv6_hl), ip6_tc, ip6_fl, ntohs(*ipv6_pl) ); }else if( detail==HIGH_DETAIL ){ Snprintf(ipinfo, sizeof(ipinfo), "ver=6, tclass=%x flow=%x payloadlen=%d nh=%s hopl=%d ", ip6_tc, ip6_fl, ntohs(*ipv6_pl), nexthdrtoa(*ipv6_nh, 1), *ipv6_hl ); } } /* TCP INFORMATION ***********************************************************/ if (nextproto == IPPROTO_TCP) { char tflags[10]; char tcpinfo[64] = ""; char buf[32]; char tcpoptinfo[256] = ""; tcp = (struct tcp_hdr *) (packet + iphdrlen); /* 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) || (len < (u32) iphdrlen + 8) ){ Snprintf(protoinfo, sizeof(protoinfo), "TCP %s:?? > %s:?? ?? %s (incomplete)", srchost, dsthost, ipinfo); } /* 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 == 8) && (len >= (u32) iphdrlen + 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(len < (u32) iphdrlen + (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=%ul win=%hu %s IP [%s]", srchost, dsthost, tflags, ntohl(tcp->th_ack), ntohs(tcp->th_win), tcpoptinfo, ipinfo); }else if( detail==HIGH_DETAIL ){ if( len >= (u32) iphdrlen + 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=%d%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, ntohs(tcp->th_win), ntohs(tcp->th_sum), 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=%ul win=%hu %s IP [%s]", srchost, dsthost, tflags, ntohl(tcp->th_ack), ntohs(tcp->th_win), tcpoptinfo, ipinfo); } } } /* 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 ((len > (u32)iphdrlen) && (len < (u32) iphdrlen + 20)) { /* We only have the first 32 bits: source and dst port */ if( (len >= (u32)iphdrlen + 4) && (len < (u32)iphdrlen + 8) ){ Snprintf(protoinfo, sizeof(protoinfo), "TCP %s:%d > %s:%d ?? (incomplete) %s", srchost, ntohs(tcp->th_sport), dsthost, ntohs(tcp->th_dport), ipinfo); } /* We only have the first 64 bits: ports and seq number */ if( (len >= (u32)iphdrlen + 8) && (len < (u32)iphdrlen + 12) ) { Snprintf(tcpinfo, sizeof(tcpinfo), "TCP %s:%d > %s:%d ?? seq=%lu (incomplete) %s", srchost, ntohs(tcp->th_sport), dsthost, ntohs(tcp->th_dport), (unsigned long) ntohl(tcp->th_seq), ipinfo); } /* We only have the first 96 bits: ports, seq and ack number */ if( (len >= (u32)iphdrlen + 12) && (len < (u32)iphdrlen + 16) ) { if(detail == LOW_DETAIL){ /* We don't print ACK in low detail */ Snprintf(tcpinfo, sizeof(tcpinfo), "TCP %s:%d > %s:%d seq=%lu (incomplete), %s", srchost, ntohs(tcp->th_sport), dsthost, ntohs(tcp->th_dport), (unsigned long) ntohl(tcp->th_seq), ipinfo); }else{ Snprintf(tcpinfo, sizeof(tcpinfo), "TCP [%s:%d > %s:%d seq=%lu ack=%lu (incomplete)] IP [%s]", srchost, ntohs(tcp->th_sport), dsthost, ntohs(tcp->th_dport), (unsigned long) ntohl(tcp->th_seq), (unsigned long) ntohl(tcp->th_ack), ipinfo); } } /* We are missing the last 32 bits (checksum and urgent pointer) */ if( (len >= (u32)iphdrlen + 16) && (len < (u32)iphdrlen + 20) ) { 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:%d > %s:%d %s %s seq=%lu win=%hu (incomplete)", srchost, ntohs(tcp->th_sport), dsthost, ntohs(tcp->th_dport), tflags, ipinfo, (unsigned long) ntohl(tcp->th_seq), ntohs(tcp->th_win)); }else if( detail == MEDIUM_DETAIL ){ Snprintf(protoinfo, sizeof(protoinfo), "TCP [%s:%d > %s:%d %s seq=%lu ack=%lu win=%hu (incomplete)] IP [%s]", srchost, ntohs(tcp->th_sport), dsthost, ntohs(tcp->th_dport), tflags, (unsigned long) ntohl(tcp->th_seq), (unsigned long) ntohl(tcp->th_ack), ntohs(tcp->th_win), ipinfo); }else if( detail == HIGH_DETAIL ){ Snprintf(protoinfo, sizeof(protoinfo), "TCP [%s:%d > %s:%d %s seq=%lu ack=%lu off=%d res=%d win=%hu (incomplete)] IP [%s]", srchost, ntohs(tcp->th_sport), dsthost, 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, 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 (len >= (u32) iphdrlen + 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(len < (u32) iphdrlen + (u32) 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:%d > %s:%d %s %s seq=%lu win=%hu %s", srchost, ntohs(tcp->th_sport), dsthost, ntohs(tcp->th_dport), tflags, ipinfo, (unsigned long) ntohl(tcp->th_seq), ntohs(tcp->th_win), tcpoptinfo); }else if( detail == MEDIUM_DETAIL ){ Snprintf(protoinfo, sizeof(protoinfo), "TCP [%s:%d > %s:%d %s seq=%lu win=%hu csum=0x%04X%s%s] IP [%s]", srchost, ntohs(tcp->th_sport), dsthost, ntohs(tcp->th_dport), tflags, (unsigned long) ntohl(tcp->th_seq), ntohs(tcp->th_win), ntohs(tcp->th_sum), (tcpoptinfo[0]!='\0') ? " " : "", tcpoptinfo, ipinfo ); }else if( detail==HIGH_DETAIL ){ Snprintf(protoinfo, sizeof(protoinfo), "TCP [%s:%d > %s:%d %s seq=%lu ack=%lu off=%d res=%d win=%hu csum=0x%04X urp=%d%s%s] IP [%s]", srchost, ntohs(tcp->th_sport), dsthost, 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, ntohs(tcp->th_win), ntohs(tcp->th_sum), ntohs(tcp->th_urp), (tcpoptinfo[0]!='\0') ? " " : "", tcpoptinfo, ipinfo ); } } else{ /* If the packet does not fall into any other category, then we have a * really screwed up packet. */ Snprintf(protoinfo, sizeof(protoinfo), "TCP %s:?? > %s:?? ?? %s (invalid TCP)", srchost, dsthost, ipinfo); } /* UDP INFORMATION ***********************************************************/ } else if (nextproto == IPPROTO_UDP && frag_off) { Snprintf(protoinfo, sizeof(protoinfo), "UDP %s:?? > %s:?? fragment %s (incomplete)", srchost, dsthost, ipinfo); } else if (nextproto == IPPROTO_UDP) { udp = (struct udp_hdr *) (packet + sizeof(struct ip)); /* 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:%d > %s:%d %s", srchost, ntohs(udp->uh_sport), dsthost, ntohs(udp->uh_dport), ipinfo); }else if( detail == MEDIUM_DETAIL ){ Snprintf(protoinfo, sizeof(protoinfo), "UDP [%s:%d > %s:%d csum=0x%04X] IP [%s]", srchost, ntohs(udp->uh_sport), dsthost, ntohs(udp->uh_dport), ntohs(udp->uh_sum), ipinfo); }else if( detail==HIGH_DETAIL ){ Snprintf(protoinfo, sizeof(protoinfo), "UDP [%s:%d > %s:%d len=%d csum=0x%04X] IP [%s]", srchost, ntohs(udp->uh_sport), dsthost, ntohs(udp->uh_dport), ntohs(udp->uh_ulen), ntohs(udp->uh_sum), ipinfo); } /* SCTP INFORMATION **********************************************************/ } else if (nextproto == IPPROTO_SCTP && frag_off) { Snprintf(protoinfo, sizeof(protoinfo), "SCTP %s:?? > %s:?? fragment %s (incomplete)", srchost, dsthost, ipinfo); } else if (nextproto == IPPROTO_SCTP) { sctp = (struct sctp_hdr *) (packet + sizeof(struct ip)); if( detail == LOW_DETAIL ){ Snprintf(protoinfo, sizeof(protoinfo), "SCTP %s:%d > %s:%d %s", srchost, ntohs(sctp->sh_sport), dsthost, ntohs(sctp->sh_dport), ipinfo); }else if( detail == MEDIUM_DETAIL ){ Snprintf(protoinfo, sizeof(protoinfo), "SCTP [%s:%d > %s:%d csum=0x%04x] IP [%s]", srchost, ntohs(sctp->sh_sport), dsthost, ntohs(sctp->sh_dport), ntohl(sctp->sh_sum), ipinfo); }else if( detail==HIGH_DETAIL ){ Snprintf(protoinfo, sizeof(protoinfo), "SCTP [%s:%d > %s:%d vtag=%ul csum=0x%08x] IP [%s]", srchost, ntohs(sctp->sh_sport), dsthost, ntohs(sctp->sh_dport), ntohl(sctp->sh_sum), ntohl(sctp->sh_vtag), ipinfo); } /* ICMP INFORMATION **********************************************************/ } else if (nextproto == IPPROTO_ICMP && frag_off) { Snprintf(protoinfo, sizeof(protoinfo), "ICMP %s > %s fragment %s (incomplete)", srchost, dsthost, ipinfo); } else if (nextproto == IPPROTO_ICMP) { struct ip *ip2; /* Points to the IP datagram carried by some ICMP messages */ char *ip2dst; /* Dest IP in caried IP datagram */ u16 *nextmtu=NULL; /* Store next hop MTU when ICMP==Frag required */ 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_mask{ u8 type; u8 code; u16 checksum; u16 id; u16 seq; u32 mask; } *icmpmask=NULL; /* Compute the length of the IP datagram + ICMP minimum length */ unsigned pktlen = (iphdrlen) + 8; /* We need the ICMP packet to be at least 8 bytes long */ if (pktlen > len) goto icmpbad; ping = (struct ppkt *) ((iphdrlen) + (char *) ip); icmppkt=(struct icmp_packet *) ((iphdrlen) + (char *) ip); switch(icmppkt->type) { /* Echo Reply **************************/ case 0: strcpy(icmptype, "Echo reply"); Snprintf(icmpfields, sizeof(icmpfields), "id=%u seq=%u", ntohs(ping->id), ntohs(ping->seq) ); break; /* Destination Unreachable *************/ case 3: /* Point to the start of the original datagram */ ip2 = (struct ip *) ((char *) ip + (iphdrlen) + 8); /* Check we have a full IP datagram included in the ICMP message */ pktlen += MAX( (ip2->ip_hl * 4), 20 ); if (pktlen > len){ if(len==(u32)(iphdrlen) + 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 becuase 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 != (u8)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) < len) { 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) < len){ if (ip2->ip_p == IPPROTO_UDP && udp) Snprintf(icmptype, sizeof icmptype, "Port %u unreachable", ntohs(udp->uh_dport)); else if (ip2->ip_p == IPPROTO_TCP && tcp) Snprintf(icmptype, sizeof icmptype, "Port %u unreachable", ntohs(tcp->th_dport)); else if (ip2->ip_p == IPPROTO_SCTP && sctp) Snprintf(icmptype, sizeof icmptype, "Port %u unreachable", 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"); nextmtu = (u16 *)(&(icmppkt->data[6])); Snprintf(icmpfields, sizeof(icmpfields), "Next-Hop-MTU=%u", ntohs(*nextmtu) ); 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=%u seq=%u", ntohs(ping->id), 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=%d", icmprouter->addrs, icmprouter->addrlen, 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=%u seq=%u orig=%lu recv=%lu trans=%lu", ntohs(icmptstamp->id), 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=%u seq=%u", ntohs(ping->id), ntohs(ping->seq) ); break; /* Information Reply *******************/ case 16: strcpy(icmptype, "Information reply"); Snprintf(icmpfields, sizeof(icmpfields), "id=%u seq=%u", ntohs(ping->id), 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_mask *)icmppkt; inet_ntop(AF_INET, &icmpmask->mask, auxbuff, sizeof(auxbuff) ); Snprintf(icmpfields, sizeof(icmpfields), "id=%u seq=%u mask=%s", ntohs(ping->id), 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 > len) { 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) ); if( detail==LOW_DETAIL ){ Snprintf(protoinfo, sizeof(protoinfo), "ICMP %s > %s %s (%s) %s", srchost, dsthost, icmptype, icmpinfo, ipinfo); }else{ Snprintf(protoinfo, sizeof(protoinfo), "ICMP [%s > %s %s (%s) %s] IP [%s]", srchost, dsthost, icmptype, icmpinfo, icmpfields, ipinfo); } } /* UNKNOWN PROTOCOL **********************************************************/ } else if( nextproto == IPPROTO_ICMPV6){ Snprintf(protoinfo, sizeof(protoinfo), "ICMPv6 (%d) %s > %s: %s", ip->ip_p, srchost, dsthost, ipinfo); }else{ if( nexthdrtoa(nextproto, 1) == NULL ){ Snprintf(protoinfo, sizeof(protoinfo), "Unknown protocol (%d) %s > %s: %s", ip->ip_p, srchost, dsthost, ipinfo); }else{ Snprintf(protoinfo, sizeof(protoinfo), "%s (%d) %s > %s: %s", nexthdrtoa(nextproto, 1), ip->ip_p, srchost, dsthost, ipinfo); } } return protoinfo; } static int match_netmask(u32 addr1, u32 addr2, u32 mask) { return (addr1 & mask) == (addr2 & mask); } static int match_netmask_bits(u32 addr1, u32 addr2, int bits) { return match_netmask(addr1, addr2, htonl((unsigned long) (0 - 1) << (32 - bits))); } static int match_netmask_bits(const struct sockaddr_in *addr1, const struct sockaddr_in *addr2, int bits) { return match_netmask_bits(addr1->sin_addr.s_addr, addr2->sin_addr.s_addr, bits); } 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; } /* 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 * const dst, struct route_nfo *rnfo, char *device, struct sockaddr_storage *spoofss) { struct interface_info *ifaces; struct interface_info *iface; int numifaces = 0; struct sys_route *routes; int numroutes = 0; int i; struct sockaddr_in *ifsin, *dstsin; char errstr[256]; errstr[0]='\0'; if (!dst) netutil_fatal("%s passed a NULL dst address", __func__); dstsin = (struct sockaddr_in *) dst; if (dstsin->sin_family != AF_INET) netutil_fatal("Sorry -- %s currently only supports IPv4", __func__); if(spoofss!=NULL){ /* Throughout the rest of this function we only change rnfo->srcaddr if the source isnt 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'){ iface = getInterfaceByName(device); if (!iface) netutil_fatal("Could not find interface %s which was specified by -e", 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; ifsin = (struct sockaddr_in *) &ifaces[i].addr; if (dstsin->sin_addr.s_addr != ifsin->sin_addr.s_addr) continue; if (iface != NULL && strcmp(ifaces[i].devname, iface->devname) != 0) 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; rnfo->ii = *loopback; rnfo->direct_connect = 1; /* But the source address we want to use is the target address. */ if (!spoofss) rnfo->srcaddr = ifaces[i].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 (!match_netmask(dstsin->sin_addr.s_addr, routes[i].dest, routes[i].netmask)) continue; /* Ignore routes that aren't on the device we specified. */ if (iface != NULL && strcmp(routes[i].device->devname, iface->devname) != 0) 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, when it exactly matches the interface address, or when it exactly matches the destination address. */ rnfo->direct_connect = (routes[i].gw.s_addr == 0) || (routes[i].gw.s_addr == ((struct sockaddr_in *) &routes[i].device->addr)->sin_addr.s_addr) || (routes[i].gw.s_addr == dstsin->sin_addr.s_addr); if (!spoofss) rnfo->srcaddr = routes[i].device->addr; ifsin = (struct sockaddr_in *) &rnfo->nexthop; ifsin->sin_family = AF_INET; ifsin->sin_addr = routes[i].gw; return 1; } /* No match on routes. Try interfaces directly. */ for (i = 0; i < numifaces; i++) { if (ifaces[i].addr.ss_family != AF_INET) continue; ifsin = (struct sockaddr_in *) &ifaces[i].addr; if (!match_netmask_bits(dstsin, ifsin, ifaces[i].netmask_bits)) continue; if (iface != NULL && strcmp(ifaces[i].devname, iface->devname) != 0) continue; rnfo->ii = ifaces[i]; rnfo->direct_connect = 1; if (!spoofss) rnfo->srcaddr = ifaces[i].addr; return 1; } return 0; } /* 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) { struct sockaddr_in *sin = (struct sockaddr_in *) to; 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_ntoa(sin->sin_addr), 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 || err == EPERM || err == EACCES || err == EMSGSIZE || err == EADDRNOTAVAIL || err == EINVAL) 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(struct eth_nfo *eth, 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, 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); memset(&sock, 0, sizeof(sock)); sock.sin_family = AF_INET; #if HAVE_SOCKADDR_SA_LEN sock.sin_len = sizeof(sock); #endif /* 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) { sock.sin_addr.s_addr = ip->ip_dst.s_addr; 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 FREEBSD || 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 FREEBSD || 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, struct eth_nfo *eth, u8 *packet, unsigned int packetlen){ if(eth) return send_ip_packet_eth(eth, packet, packetlen); else return send_ip_packet_sd(sd, 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, struct eth_nfo *eth, 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, 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, fpacket, ntohs(ip->ip_len)); if (res == -1) break; } free(fpacket); return res; } #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; 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; } // 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; } } // 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 0 and fills in pcapdev if successful. */ if (eth_get_pcap_devname(dnetdev, tmpdev, sizeof(tmpdev)) != 0) 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 /* Compute exponential sleep time for my_pcap_open_live(). Returned * value is 5 to the times-th power (5^times) */ static unsigned int compute_sleep_time(unsigned int times){ unsigned int i=0; unsigned int result=1; for(i=0; i= 3) { return NULL; } else { netutil_error("pcap_open_live(%s, %d, %d, %d) FAILED. Reported error: %s. Will wait %d seconds then retry.", pcapdev, snaplen, promisc, to_ms, err0r, compute_sleep_time(failed)); } sleep( compute_sleep_time(failed) ); } } while (!pt); #ifdef WIN32 /* We want any responses back ASAP */ pcap_setmintocopy(pt, 1); #endif return pt; } /* Set a pcap filter */ void set_pcap_filter(const char *device, pcap_t *pd, const char *bpf, ...) { va_list ap; char buf[3072]; struct bpf_program fcode; #ifndef __amigaos__ unsigned int localnet, netmask; #else bpf_u_int32 localnet, netmask; #endif char err0r[256]; // Cast below is becaue OpenBSD apparently has a version that takes a // non-const device (hopefully they don't actually write to it). if (pcap_lookupnet((char *) device, &localnet, &netmask, err0r) < 0) netutil_fatal("Failed to lookup subnet/netmask for device (%s): %s", device, err0r); va_start(ap, bpf); if (Vsnprintf(buf, sizeof(buf), bpf, ap) >= (int) sizeof(buf)) netutil_fatal("%s called with too-large filter arg\n", __func__); va_end(ap); /* Due to apparent bug in libpcap */ /* Maybe this bug no longer exists ... I'll comment out for now * if (islocalhost(target->v4hostip())) * buf[0] = '\0'; */ if (pcap_compile(pd, &fcode, buf, 0, netmask) < 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); } /* Returns true if the captured frame is ARP. This function understands the datalink types DLT_EN10MB and DLT_LINUX_SLL. */ static bool frame_is_arp(const u8 *frame, int datalink) { if (datalink == DLT_EN10MB) { return ntohs(*((u16 *) (frame + 12))) == ETH_TYPE_ARP; } else if (datalink == DLT_LINUX_SLL) { return ntohs(*((u16 *) (frame + 2))) == ARPHRD_ETHER && /* sll_hatype */ ntohs(*((u16 *) (frame + 4))) == 6 && /* sll_halen */ ntohs(*((u16 *) (frame + 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 (*traceArp_callback)(int, const u8 *, u32 , struct timeval *)) { static int warning = 0; int datalink; struct pcap_pkthdr head; u8 *p; int timedout = 0; int badcounter = 0; unsigned int offset=0; struct timeval tv_start, tv_end; 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)); if (datalink == DLT_EN10MB) { offset = ETH_HDR_LEN; } else if (datalink == DLT_LINUX_SLL) { /* The datalink type is Linux "cooked" sockets. See pcap-linktype(7). */ offset = 16; } else { netutil_fatal("%s called on interface that is datatype %d rather than DLT_EN10MB (%d) or DLT_LINUX_SLL (%d)", __func__, datalink, DLT_EN10MB, DLT_LINUX_SLL); } if (to_usec > 0) { gettimeofday(&tv_start, NULL); } do { #ifdef WIN32 if (to_usec == 0) { PacketSetReadTimeout(pd->adapter, 1); } else { gettimeofday(&tv_end, NULL); long to_left = MAX(1, (to_usec - TIMEVAL_SUBTRACT(tv_end, tv_start)) / 1000); // Set the timeout (BUGBUG: this is cheating) PacketSetReadTimeout(pd->adapter, to_left); } #endif p = NULL; if (pcap_select(pd, to_usec) == 0) timedout = 1; else p = (u8 *) pcap_next(pd, &head); if (p && head.caplen >= offset + 28) { /* hw type eth (0x0001), prot ip (0x0800), hw size (0x06), prot size (0x04) */ if (frame_is_arp(p, datalink) && memcmp(p + offset, "\x00\x01\x08\x00\x06\x04\x00\x02", 8) == 0) { memcpy(sendermac, p + offset + 8, 6); /* I think alignment should allow this ... */ memcpy(&senderIP->s_addr, p + offset + 14, 4); break; } } if (!p) { /* 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) { // FIXME: I eventually need to figure out why Windows head.ts time is sometimes BEFORE the time I // sent the packet (which is according to gettimeofday() in nbase). For now, I will sadly have to // use gettimeofday() for Windows in this case // Actually I now allow .05 discrepancy. So maybe this isn't needed. I'll comment out for now. // Nope: it is still needed at least for Windows. Sometimes the time from he 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 } if(traceArp_callback!=NULL){ /* TODO: First parameter "2" is a hardcoded value for Nmap's PacketTrace::RECV*/ traceArp_callback(2, (u8 *) p + offset, ARP_HDR_LEN + ARP_ETHIP_LEN, rcvdtime); } return 1; } /* 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 */ char *grab_next_host_spec(FILE *inputfd, bool random, int argc, char **fakeargv) { 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)? fakeargv[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); }