diff options
Diffstat (limited to '')
-rw-r--r-- | nping/utils_net.cc | 1941 |
1 files changed, 1941 insertions, 0 deletions
diff --git a/nping/utils_net.cc b/nping/utils_net.cc new file mode 100644 index 0000000..e7acbad --- /dev/null +++ b/nping/utils_net.cc @@ -0,0 +1,1941 @@ + +/*************************************************************************** + * utils_net.cc -- Miscellaneous network-related functions that perform * + * various tasks. * + * * + ***********************IMPORTANT NMAP LICENSE TERMS************************ + * + * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap + * Project"). Nmap is also a registered trademark of the Nmap Project. + * + * This program is distributed under the terms of the Nmap Public Source + * License (NPSL). The exact license text applying to a particular Nmap + * release or source code control revision is contained in the LICENSE + * file distributed with that version of Nmap or source code control + * revision. More Nmap copyright/legal information is available from + * https://nmap.org/book/man-legal.html, and further information on the + * NPSL license itself can be found at https://nmap.org/npsl/ . This + * header summarizes some key points from the Nmap license, but is no + * substitute for the actual license text. + * + * Nmap is generally free for end users to download and use themselves, + * including commercial use. It is available from https://nmap.org. + * + * The Nmap license generally prohibits companies from using and + * redistributing Nmap in commercial products, but we sell a special Nmap + * OEM Edition with a more permissive license and special features for + * this purpose. See https://nmap.org/oem/ + * + * If you have received a written Nmap license agreement or contract + * stating terms other than these (such as an Nmap OEM license), you may + * choose to use and redistribute Nmap under those terms instead. + * + * The official Nmap Windows builds include the Npcap software + * (https://npcap.com) for packet capture and transmission. It is under + * separate license terms which forbid redistribution without special + * permission. So the official Nmap Windows builds may not be redistributed + * without special permission (such as an Nmap OEM license). + * + * Source is provided to this software because we believe users have a + * right to know exactly what a program is going to do before they run it. + * This also allows you to audit the software for security holes. + * + * Source code also allows you to port Nmap to new platforms, fix bugs, and add + * new features. You are highly encouraged to submit your changes as a Github PR + * or by email to the dev@nmap.org mailing list for possible incorporation into + * the main distribution. Unless you specify otherwise, it is understood that + * you are offering us very broad rights to use your submissions as described in + * the Nmap Public Source License Contributor Agreement. This is important + * because we fund the project by selling licenses with various terms, and also + * because the inability to relicense code has caused devastating problems for + * other Free Software projects (such as KDE and NASM). + * + * The free version of Nmap is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, + * indemnification and commercial support are all available through the + * Npcap OEM program--see https://nmap.org/oem/ + * + ***************************************************************************/ +#include "nping.h" +#include "utils.h" +#include "utils_net.h" +#include "NpingOps.h" +#include "global_structures.h" +#include "output.h" +#include "nbase.h" +#include "pcap.h" +#include "dnet.h" +#include <vector> + +extern NpingOps o; + +int atoIP(const char *hostname, struct in_addr *dst){ + struct sockaddr_in i; + unsigned int stlen=0; + if ( resolve(hostname, 0, (sockaddr_storage*)&i, (size_t *)&stlen , PF_INET) != 0 ) + return OP_FAILURE; + *dst=i.sin_addr; + return OP_SUCCESS; +} /* End of atoIP */ + +int atoIP(const char *hostname, struct sockaddr_storage *ss, int family){ + size_t stlen=0; + if(ss==NULL || hostname==NULL) + return OP_FAILURE; + if(family!=AF_INET && family!=AF_INET6) + return OP_FAILURE; + if ( resolve(hostname, 0, ss, &stlen , family) != 0 ) + return OP_FAILURE; + return OP_SUCCESS; +} /* End of atoIP */ + + +/** @warning The string is returned in a statically allocated buffer, which + * subsequent calls will overwrite.*/ +char *IPtoa(u32 i){ + static char buffer[24]; + char *aux=NULL; + memset(buffer, 0, 24); + struct in_addr myip; + myip.s_addr=i; + aux=inet_ntoa(myip); + /* Get our own copy of the data so only subsequent calls to IPtoa overwrite + * the returned buffer (not subsequent calls to inet_ntoa() made by other + * methods. */ + if(aux!=NULL){ + strncpy(buffer, aux, 23); + return buffer; + } + else + return NULL; +} /* End of IPtoa() */ + + +/** @warning The string is returned in a statically allocated buffer, which + * subsequent calls will overwrite.*/ +char *IPtoa(struct sockaddr_storage *ss){ + struct sockaddr_in *s4=(struct sockaddr_in *)ss; + struct sockaddr_in6 *s6=(struct sockaddr_in6 *)ss; + static char ipstring[256]; + memset(ipstring, 0, 256); + if( ss==NULL ){ + snprintf(ipstring,256, "[[NULL address supplied to IPtoa()]]"); + return ipstring; + } + if(s6->sin6_family==AF_INET6){ + inet_ntop(AF_INET6, &s6->sin6_addr, ipstring, sizeof(ipstring)); + }else if( s4->sin_family == AF_INET ) { + inet_ntop(AF_INET, &s4->sin_addr, ipstring, sizeof(ipstring)); + }else{ + snprintf(ipstring,256,"[[Unknown address family sockaddr supplied to IPtoa()]]"); + } + return ipstring; +} /* End of IPtoa() */ + + +char *IPtoa(struct sockaddr_storage ss){ + return IPtoa(&ss); +} /* End of IPtoa() */ + +char *IPtoa(struct sockaddr_storage *ss, int family){ + if(ss==NULL){ + return NULL; + }else if(family==AF_INET){ + struct sockaddr_in *s4=(struct sockaddr_in *)ss; + return IPtoa(s4->sin_addr); + }else if(family==AF_INET6){ + struct sockaddr_in6 *s6=(struct sockaddr_in6 *)ss; + return IPtoa(s6->sin6_addr); + }else{ + return NULL; + } +} /* End of IPtoa() */ + + +/** @warning The string is returned in a statically allocated buffer, which + * subsequent calls will overwrite.*/ +char *IPtoa(struct in_addr addr){ + static char ipstring[256]; + memset(ipstring, 0, 256); + inet_ntop(AF_INET, &addr, ipstring, sizeof(ipstring)); + return ipstring; +} /* End of IPtoa() */ + + +/** @warning The string is returned in a statically allocated buffer, which + * subsequent calls will overwrite.*/ +char *IPtoa(struct in6_addr addr){ + static char ipstring[256]; + memset(ipstring, 0, 256); + inet_ntop(AF_INET6, &addr, ipstring, sizeof(ipstring)); + return ipstring; +} /* End of IPtoa() */ + + +/** @warning The string is returned in a statically allocated buffer, which + * subsequent calls will overwrite.*/ +char *IPtoa(u8 *ipv6addr){ + static char ipstring[256]; + memset(ipstring, 0, 256); + struct in6_addr s6; + memcpy(s6.s6_addr, ipv6addr, 16); + inet_ntop(AF_INET6, &s6, ipstring, sizeof(ipstring)); + return ipstring; +} /* End of IPtoa() */ + + +/** Returns true if supplied value corresponds to a valid RFC compliant ICMP + * type. Otherwise it returns false. */ +bool isICMPType(u8 type){ + switch (type){ + case 0: + case 3: + case 4: + case 5: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 30: + return true; + break; + + default: + return false; + break; + } + return false; +} /* End of isICMPType() */ + + +u16 sockaddr2port(struct sockaddr_storage ss){ + return sockaddr2port(&ss); +} + + +u16 sockaddr2port(struct sockaddr_storage *ss){ + assert(ss!=NULL); + if(ss->ss_family==AF_INET) + return sockaddr2port( (struct sockaddr_in *)ss ); + else if( ss->ss_family==AF_INET6){ + return sockaddr2port( (struct sockaddr_in6 *)ss ); + }else{ + return 0; + } +} + + +u16 sockaddr2port(struct sockaddr_in *s4){ + assert(s4!=NULL); + return ntohs(s4->sin_port); +} + + +u16 sockaddr2port(struct sockaddr_in6 *s6){ + assert(s6!=NULL); + return ntohs(s6->sin6_port); +} + + +/* Sets the address family member of the supplied sockaddr. */ +int setsockaddrfamily(struct sockaddr_storage *ss, int family){ + struct sockaddr_in *s4=(struct sockaddr_in *)ss; + s4->sin_family=family; + return OP_SUCCESS; +} /* End of setsockaddrfamily() */ + + +/* Sets the special INADDR_ANY or in6addr_an constant on the sin_family or + * sin6_addr member of the supplied sockaddr. Note that for this to work, + * the supplied sockaddr_storage MUST have a correct address family set + * already (sin_family or sin6_family). */ +int setsockaddrany(struct sockaddr_storage *ss){ + struct sockaddr_in *s4=(struct sockaddr_in *)ss; + struct sockaddr_in6 *s6=(struct sockaddr_in6 *)ss; + if(s4->sin_family==AF_INET) + s4->sin_addr.s_addr=INADDR_ANY; + else if(s6->sin6_family==AF_INET6) + s6->sin6_addr=in6addr_any; + else + return OP_FAILURE; + return OP_SUCCESS; +} /* End of setsockaddrany() */ + + +/** Returns true if supplied value corresponds to a valid RFC compliant ICMP + * Code. Otherwise it returns false. + * @warning The fact that a given value matches a standard code does not + * mean the code is correct because it depends on the type being used */ +bool isICMPCode(u8 code){ + /* Correct as of 25 June 09. + * http://www.iana.org/assignments/icmp-parameters */ + if( code<=16 ) + return true; + else + return false; +} /* End of isICMPType() */ + + + +/** Returns true if supplied value corresponds to a valid RFC compliant ICMP + * Code for the supplied type + * @warning The fact that a given value matches a standard code does not + * mean the code is correct because it depends on the type being used */ +bool isICMPCode(u8 code, u8 type){ + /* Correct as of 25 June 09. + * http://www.iana.org/assignments/icmp-parameters */ + switch (type){ + case 0: /* Echo Reply */ + if(code==0) return true; + break; + + case 3: /* Destination Unreachable */ + if(code<=15) return true; + break; + + case 4: /* Source Quench */ + if(code==0) return true; + break; + + case 5: /* Redirect */ + if(code<=3) return true; + break; + + case 6: /* Alternate Address for Host */ + if(code==0) return true; + break; + + case 8: /* Echo */ + if(code==0) return true; + break; + + case 9: /* Router Advertisement */ + if(code==0 || code==16) return true; + break; + + case 10: /* Router Selection */ + if(code==0) return true; + break; + + case 11: /* Time Exceeded */ + if(code==0 || code==1) return true; + break; + + case 12: /* Parameter Problem */ + if(code<=2) return true; + break; + + case 13: /* Timestamp */ + if(code==0) return true; + break; + + case 14: /* Timestamp Reply */ + if(code==0) return true; + break; + + case 15: /* Information Request */ + if(code==0) return true; + break; + + case 16: /* Information Reply */ + if(code==0) return true; + break; + + case 17: /* Address Mask Request */ + if(code==0) return true; + break; + + case 18: /* Address Mask Reply */ + if(code==0) return true; + break; + + case 30: /* Traceroute */ + return true; + break; + + case 40: /* Experimental ICMP Security Failures Messages [RFC 2521] */ + if(code<=5) return true; + break; + + default: + return false; + break; + } + return false; +} /* End of isICMPType() */ + + +/* This function fills buffer "dstbuff" with a printable string that + * represents the supplied packet. When sending IPv6 packet at raw TCP + * level, the caller may specify source and/or destination address so they + * also get included in the returned information. However, this is optional + * and is safe to pass NULL values. */ +int getPacketStrInfo(const char *proto, const u8 *packet, u32 len, u8 *dstbuff, + u32 dstlen, struct sockaddr_storage *ss_src, struct sockaddr_storage *ss_dst){ + char *b=NULL; + int detail; + + if ( dstbuff == NULL || dstlen < 512 ) + nping_fatal(QT_3,"safe_ippackethdrinfo() Invalid values supplied."); + + if(o.getVerbosity()>=VB_2) + detail=HIGH_DETAIL; + else if (o.getVerbosity()==VB_1) + detail=MEDIUM_DETAIL; + else + detail=LOW_DETAIL; + + if( !strcasecmp(proto, "IP") || !strcasecmp(proto, "IPv4") || !strcasecmp(proto, "IPv6")){ + b=(char *)ippackethdrinfo(packet, len, detail); + strncpy((char*)dstbuff, b, dstlen); + dstbuff[dstlen-1]=0; /* Just to be sure, NULL-terminate the last position*/ + }else if( !strcasecmp(proto, "ARP") || !strcasecmp(proto, "RARP") ){ + return arppackethdrinfo(packet, len, dstbuff, dstlen); + }else if( !strcasecmp(proto, "IPv6_NO_HEADER") || o.ipv6UsingSocket() ){ + if( o.getMode()==TCP ) + return tcppackethdrinfo(packet, len, dstbuff, dstlen, detail, ss_src, ss_dst); + else if ( o.getMode()==UDP ) + return udppackethdrinfo(packet, len, dstbuff, dstlen, detail, ss_src, ss_dst); + else + nping_fatal(QT_3, "getPacketStrInfo(): Unable to determinate transport layer protocol"); + }else{ + nping_fatal(QT_3, "getPacketStrInfo(): Unknown protocol"); + } + return OP_SUCCESS; +} /* getPacketStrInfo() */ + + +/* Same as previous one but passes NULL sockaddr values automatically. */ +int getPacketStrInfo(const char *proto, const u8 *packet, u32 len, u8 *dstbuff, u32 dstlen){ + return getPacketStrInfo(proto,packet,len,dstbuff,dstlen,NULL,NULL); +} /* getPacketStrInfo() */ + + +/** This function converts a port ranges specification into an array of u16 + * integers that represent each of the specified ports. It allocates space + * for the port lists and stores the pointer in the supplied "list" parameter. + * Also, the number of ports in the array is returned through the supplied + * "count" pointer. + * @warning the caller is the one responsible for free()ing the allocated + * list of ports. + */ +int nping_getpts_simple(const char *origexpr, u16 **list, int *count) { + u8 *porttbl; + int portwarning = 0; + int i, j; + + /* Allocate array to hold 2^16 ports */ + porttbl = (u8 *) safe_zalloc(65536); + + /* Get the ports but do not allow changing the type with T:, U:, or P:. */ + getpts_aux(origexpr, 0, porttbl, &portwarning); + + /* Count how many are set. */ + *count = 0; + for (i = 0; i <= 65535; i++) { + if (porttbl[i]) + (*count)++; + } + + if (*count == 0){ + free(porttbl); + return OP_FAILURE; + } + + *list = (unsigned short *) safe_zalloc(*count * sizeof(u16)); + + /* Fill in the list. */ + for (i = 0, j = 0; i <= 65535; i++) { + if (porttbl[i]) + (*list)[j++] = i; + } + free(porttbl); + return OP_SUCCESS; +} /* End of nping_getpts_simple() */ + + + + +/** Determines the net iface that should be used when sending packets + * to "destination". + * @return OP_SUCCESS on success and OP_FAILUIRE in case of error. + * @warning "*dev" must be able to hold at least 16 bytes */ +int getNetworkInterfaceName(u32 destination, char *dev){ + struct route_nfo rnfo; + struct sockaddr_in dst, src; + bool result=false; + if(dev==NULL) + nping_fatal(QT_3, "getNetworkInterfaceName(): NULL value supplied."); + memset(&rnfo, 0, sizeof(struct route_nfo) ); + memset(&dst, 0, sizeof(struct sockaddr_in) ); + memset(&src, 0, sizeof(struct sockaddr_in) ); + dst.sin_addr.s_addr = destination; + dst.sin_family = AF_INET; + result=route_dst((struct sockaddr_storage *)&dst, &rnfo, NULL, NULL); + if( result == false ) + return OP_FAILURE; + strncpy( dev, rnfo.ii.devname, 16 ); + return OP_SUCCESS; +} /* End of getSourceAddress() */ + + + +/** Determines the net iface that should be used when sending packets + * to "destination". + * @return OP_SUCCESS on success and OP_FAILUIRE in case of error. + * @warning "*dev" must be able to hold at least 16 bytes */ +int getNetworkInterfaceName(struct sockaddr_storage *dst, char *dev){ + struct route_nfo rnfo; + struct sockaddr_storage src; + bool result=false; + if(dev==NULL) + nping_fatal(QT_3, "getNetworkInterfaceName(): NULL value supplied."); + memset(&rnfo, 0, sizeof(struct route_nfo) ); + memset(&src, 0, sizeof(struct sockaddr_in) ); + result=route_dst(dst, &rnfo, NULL, NULL); + if( result == false ) + return OP_FAILURE; + strncpy( dev, rnfo.ii.devname, 16 ); + return OP_SUCCESS; +} /* End of getSourceAddress() */ + + +typedef struct cached_host{ + char hostname[MAX_CACHED_HOSTNAME_LEN]; + struct sockaddr_storage ss; + size_t sslen; +}cached_host_t; + + +int resolveCached(char *host, struct sockaddr_storage *ss, size_t *sslen, int pf) { + static cached_host_t archive[MAX_CACHED_HOSTS]; + static int cached_count=0; + static int current_index=0; /* Used when we reach the end of the array and we do circular buffer */ + int result=0; + //static int way=1; + static int misses=0, hits=0; + + /* Used for debug. When called with NULL,0x1337, print stats */ + if(host==NULL && pf == 1337){ + nping_print(DBG_4, "resolveCached(): MISSES: %d, HITS: %d\n", misses, hits); + return OP_SUCCESS; + } + + + if( ss==NULL || sslen==NULL || host==NULL) + nping_fatal(QT_3, "resolveCached(): NULL values supplied"); + + /* First we check if we have the host already cached */ + for(int i=0; i<MAX_CACHED_HOSTS && i<cached_count; i++){ + if( !strcasecmp( archive[i].hostname , host ) ){ /* Cache hit */ + *sslen=archive[i].sslen; + memcpy(ss, &(archive[i].ss) , *sslen); + hits++; + nping_print(DBG_4, "resolveCached(): Cache hit %d for %s\n", hits, host); + return OP_SUCCESS; + } + } + + /* Cache miss */ + misses++; + nping_print(DBG_4, "resolveCached(): Cache miss %d for %s\n", misses, host); + + if( (result=resolve(host, 0, ss, sslen, pf)) == 0 ){ + + /* Increment count */ + if( cached_count < MAX_CACHED_HOSTS ) + cached_count++; + + /* Store info */ + memset(&(archive[current_index]), 0, sizeof(cached_host_t) ); + strncpy(archive[current_index].hostname, host, MAX_CACHED_HOSTNAME_LEN); + archive[current_index].sslen = *sslen; + memcpy(&(archive[current_index].ss), ss, *sslen); + + + /* I run some tests to see what is the best approach when the cache + * is full. The thing is that in Nping, we are likely to call + * this function over and over with specifying the same hosts. Deleting + * the oldest entry results in 100% cache misses. I also tried to start + * overwriting entries first backwards and then upwards. That showed + * much better results. However, if we simply overwrite the last + * cache entry over an over we get the best results. */ + if( current_index < MAX_CACHED_HOSTS-1 ) + current_index++; + return 0; + + + + ///* Watch out for the overflow. If cache is full, */ + //if( cached_count == MAX_CACHED_HOSTS ){ + //if( way%2==1 ){ + //if( current_index > 0 ) + //current_index--; + //else{ + //current_index=1; + //way++; + //} + //} + //else{ + //if( current_index < MAX_CACHED_HOSTS-1 ) + //current_index++; + //else{ + //current_index=MAX_CACHED_HOSTS-2; + //way++; + //} + //} + //} + //else + //current_index++; + //return OP_SUCCESS; + + }else{ + nping_warning(QT_2, "Error resolving %s\n",host); + return OP_FAILURE; + } +} /* End of resolveCached() */ + + +typedef struct gethostbyname_cached{ + char hostname[MAX_CACHED_HOSTNAME_LEN]; + struct hostent *h; +}gethostbynamecached_t; + + +struct hostent *gethostbynameCached(char *host){ + static gethostbynamecached_t archive[MAX_CACHED_HOSTS]; + static int cached_count=0; + static int current_index=0; + struct hostent *result=NULL; + static int misses=0, hits=0; + int i=0; + + if( host==NULL) + nping_fatal(QT_3, "gethostbynameCached(): NULL values supplied"); + + /* First we check if we have the host already cached */ + for(i=0; i<MAX_CACHED_HOSTS && i<cached_count; i++){ + if( !strcasecmp( archive[i].hostname , host ) ){ /* Cache hit */ + hits++; + nping_print(DBG_4, "gethostbynameCached(): Cache hit %d for %s", hits, host); + return archive[i].h; + } + } + + /* Cache miss */ + misses++; + nping_print(DBG_4, "gethostbynameCached(): Cache miss %d for %s", misses, host); + + if( (result=gethostbyname(host) ) != NULL ){ + + /* Increment cache entry count */ + if( cached_count < MAX_CACHED_HOSTS ) + cached_count++; + + /* If we've reached the max number of cached hosts, free the + * hostent entry that is in the last slot so we can insert a new + * one in its place */ + if ( current_index==MAX_CACHED_HOSTS-1 && archive[current_index].h != NULL ) + hostentfree( archive[current_index].h ); + + /* Store the hostent entry in the cache */ + memset(&(archive[current_index]), 0, sizeof(gethostbynamecached_t) ); + strncpy(archive[current_index].hostname, host, MAX_CACHED_HOSTNAME_LEN); + archive[current_index].h = hostentcpy( result ); + + /* Return the entry that we've just added */ + if( current_index < MAX_CACHED_HOSTS-1 ){ + current_index++; + return archive[current_index-1].h; + } + else{ + return archive[current_index].h; + } + + }else{ + return NULL; + } +} /* End of resolveCached() */ + + +struct hostent *hostentcpy(struct hostent *src){ + struct hostent *st=NULL; + int aliases=0; + int addrs=0; + + if( src == NULL ) + return NULL; + + st=(struct hostent *)safe_zalloc( sizeof(struct hostent) ); + + /* Copy host name */ + if( src->h_name!= NULL ) + st->h_name = strdup( src->h_name ); + + /* Copy aliases */ + if( src->h_aliases != NULL ){ + while( src->h_aliases[aliases] ) /* Fist count how many*/ + aliases++; + st->h_aliases = (char **)safe_zalloc( aliases * sizeof(char*) ); /* Allocate array */ + for( int i=0; i<aliases; i++) /* Copy all entries */ + st->h_aliases[i] = strdup( src->h_aliases[i] ); + } + /* Copy address type an length */ + st->h_addrtype=src->h_addrtype; + st->h_length=src->h_length; + + /* Copy list of addresses */ + if( src->h_addr_list != NULL ){ + + while( src->h_addr_list[addrs] ) /* Fist count how many*/ + addrs++; + + st->h_addr_list = (char **)safe_zalloc( addrs * sizeof(char*) ); /* Allocate array */ + + for( int j=0; j<addrs; j++) /* Copy all entries */ + st->h_addr_list[j] = strdup( src->h_addr_list[j] ); + + /* Create dummy synonym for h_addr_list[0]*/ + st->h_addr=st->h_addr_list[0]; + } + return st; +} /* End of hostentcpy() */ + + +/** Free a hostend structure. + * @warning This function can ONLY be used with hostent structs returned by + * hostentcpy. Do NOT attempt to use this on a hostent returned by + * gethostbyname() because the structure may contain pointers to statically + * allocated memory regions.*/ +int hostentfree(struct hostent *src){ + int aliases=0; + int addrs=0; + + if( src == NULL ) + return OP_SUCCESS; + + /* Free host name */ + if ( src->h_name != NULL ) + free( src->h_name ); + + /* Free aliases */ + if( src->h_aliases != NULL ){ + while( src->h_aliases[aliases] ){ + free(src->h_aliases[aliases]); + aliases++; + } + free(src->h_aliases); + } + + /* Free list of addresses */ + if( src->h_addr_list != NULL ){ + + while( src->h_addr_list[addrs] ){ + addrs++; + free( src->h_addr_list[addrs] ); + } + free( src->h_addr_list ); + } + + /* Finally free the base hostent struct */ + free( src ); + return OP_SUCCESS; +} /* End of hostentfree() */ + + + +/** Receives a MAC address as a string of format 00:13:01:e6:c7:ae or + * 00-13-01-e6-c7-ae and stores in targetbuff the 6 corresponding bytes. + * The "txt" parameter may take the special value "rand" or "random", + * in which case, 6 random bytes will be stored in "targetbuff". + * @return OP_SUCCESS on success and OP_FAILURE in case of error. + * Buffer targetbuff is NOT modified if "txt" does not have the proper + * format */ +int parseMAC(const char *txt, u8 *targetbuff){ + u8 mac_data[6]; + char tmphex[3]; + int i=0, j=0; + + if( txt==NULL || targetbuff==NULL ) + return OP_FAILURE; + + /* Set up a random MAC if user requested so. */ + if( meansRandom(txt) ){ + get_random_bytes(targetbuff, 6); + return OP_SUCCESS; + /* Or set it to FF:FF:FF:FF:FF:FF if user chose broadcast */ + }else if( !strcasecmp(optarg, "broadcast") || !strcasecmp(optarg, "bcast") ){ + memset(targetbuff, 0xFF, 6); + return OP_SUCCESS; + } + + /* Array should look like 00:13:01:e6:c7:ae or 00-13-01-e6-c7-ae + Array positions: 01234567890123456 01234567890123456 */ + if( strlen(txt)!=17 ) + return OP_FAILURE; + /* Check MAC has the correct ':' or '-' characters */ + if( (txt[2]!=':' && txt[2]!='-') || (txt[5]!=':' && txt[5]!='-') || + (txt[8]!=':' && txt[8]!='-') || (txt[11]!=':' && txt[11]!='-') || + (txt[14]!=':' && txt[14]!='-') ) + return OP_FAILURE; + + /* Convert txt into actual bytes */ + for(i=0, j=0; i<6; i++, j+=3 ){ + if( !isxdigit(txt[j]) || !isxdigit(txt[j+1]) ) + return OP_FAILURE; + tmphex[0] = txt[j]; + tmphex[1] = txt[j+1]; + tmphex[2] = '\0'; + mac_data[i] = (u8) strtol(tmphex, NULL, 16); + } + memcpy(targetbuff, mac_data, 6); + return OP_SUCCESS; +} /* End of parseMAC() */ + + + +char *MACtoa(u8 *mac){ + static char macinfo[24]; + memset(macinfo, 0, 24); + sprintf(macinfo,"%02X:%02X:%02X:%02X:%02X:%02X", + mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]); + return macinfo; +} /* End of MACtoa() */ + + + + +/* Returns a buffer of ASCII information about an ARP/RARP packet that may look + like "ARP who has 192.168.10.1? Tell 192.168.10.98" + Since this is a static buffer, don't use threads or call twice + within (say) printf(). And certainly don't try to free() it! The + returned buffer is NUL-terminated */ +const char *arppackethdrinfo(const u8 *packet, u32 len, int detail){ + static char protoinfo[512]; + if (packet==NULL) + nping_fatal(QT_3, "arppackethdrinfo(): NULL value supplied"); + if( len < 28 ) + return "BOGUS! Packet too short."; + u16 *htype = (u16 *)packet; + u16 *ptype = (u16 *)(packet+2); + u8 *hlen = (u8 *)(packet+4); + u8 *plen = (u8 *)(packet+5); + u16 *op = (u16 *)(packet+6); + u8 *sMAC= (u8 *)(packet+8); + u32 *sIP = (u32 *)(packet+14); + u8 *tMAC = (u8 *)(packet+18); + u32 *tIP = (u32 *)(packet+24); + + if( ntohs(*op) == 1 ){ /* ARP Request */ + sprintf(protoinfo, "ARP who has %s? ", IPtoa(*tIP)); + sprintf(protoinfo+strlen(protoinfo),"Tell %s", IPtoa(*sIP) ); + } + else if( ntohs(*op) == 2 ){ /* ARP Reply */ + sprintf(protoinfo, "ARP reply %s ", IPtoa(*sIP)); + sprintf(protoinfo+strlen(protoinfo),"is at %s", MACtoa(sMAC) ); + } + else if( ntohs(*op) == 3 ){ /* RARP Request */ + sprintf(protoinfo, "RARP who is %s? Tell %s", MACtoa(tMAC), MACtoa(sMAC) ); + } + else if( ntohs(*op) ==4 ){ /* RARP Reply */ + sprintf(protoinfo, "RARP reply: %s is at %s", MACtoa(tMAC), IPtoa(*tIP) ); + } + else{ + sprintf(protoinfo, "HTYPE:%04X PTYPE:%04X HLEN:%d PLEN:%d OP:%04X SMAC:%s SIP:%s DMAC:%s DIP:%s", + *htype, *ptype, *hlen, *plen, *op, MACtoa(sMAC), IPtoa(*sIP), MACtoa(tMAC), IPtoa(*tIP)); + } + return protoinfo; +} /* End of arppackethdrinfo() */ + + + + +int arppackethdrinfo(const u8 *packet, u32 len, u8 *dstbuff, u32 dstlen){ + char *b=NULL; + int detail=0; + + if ( dstbuff == NULL || dstlen < 512 ) + nping_fatal(QT_3,"safe_arppackethdrinfo() Invalid values supplied."); + + /* Determine level of detail in packet output from current verbosity level */ + if(o.getVerbosity()>=VB_2) + detail=HIGH_DETAIL; + else if (o.getVerbosity()==VB_1) + detail=MEDIUM_DETAIL; + else + detail=LOW_DETAIL; + + b=(char *)arppackethdrinfo(packet, len, detail); + strncpy((char*)dstbuff, b, dstlen); + dstbuff[dstlen-1]=0; /* Just to be sure, NULL-terminate the last position*/ + return OP_SUCCESS; +} /* End of arppackethdrinfo() */ + + + +int tcppackethdrinfo(const u8 *packet, size_t len, u8 *dstbuff, size_t dstlen, + int detail, struct sockaddr_storage *src, struct sockaddr_storage *dst){ + + struct tcp_hdr *tcp=NULL; ; /* TCP header structure. */ + char *p = NULL; /* Aux pointer. */ + static char protoinfo[1024] = ""; /* Stores final info string. */ + char tflags[10]; + char tcpinfo[64] = ""; + char buf[32]; + char tcpoptinfo[256] = ""; + struct sockaddr_in *s4=(struct sockaddr_in *)src; + struct sockaddr_in6 *s6=(struct sockaddr_in6 *)src; + struct sockaddr_in *d4=(struct sockaddr_in *)dst; + struct sockaddr_in6 *d6=(struct sockaddr_in6 *)dst; + char srcipstring[128]; + char dstipstring[128]; + + assert(packet); + assert(dstbuff); + assert(len>=20); + + tcp=(struct tcp_hdr *)packet; + + /* Ensure we end up with a valid detail number */ + if( detail!=LOW_DETAIL && detail!=MEDIUM_DETAIL && detail!=HIGH_DETAIL) + detail=LOW_DETAIL; + + + /* Determine target IP address */ + if(src!=NULL){ + if( s4->sin_family==AF_INET ){ + inet_ntop(AF_INET, &s4->sin_addr, srcipstring, sizeof(srcipstring)); + } + else if( s6->sin6_family==AF_INET6){ + inet_ntop(AF_INET6, &s6->sin6_addr, srcipstring, sizeof(srcipstring)); + }else{ + sprintf(dstipstring, "unknown_addr_family"); + } + }else{ + sprintf(srcipstring, "this_host"); + } + + /* Determine source IP address */ + if(dst!=NULL){ + if( d4->sin_family==AF_INET ){ + inet_ntop(AF_INET, &d4->sin_addr, dstipstring, sizeof(dstipstring)); + } + else if( d6->sin6_family==AF_INET6){ + inet_ntop(AF_INET6, &d6->sin6_addr, dstipstring, sizeof(dstipstring)); + }else{ + sprintf(dstipstring, "unknown_addr_family"); + } + }else{ + sprintf(dstipstring, "unknown_host"); + } + + /* 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) 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 seq=%lu win=%hu %s", + srcipstring, ntohs(tcp->th_sport), dstipstring, ntohs(tcp->th_dport), + tflags, (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]", + srcipstring, ntohs(tcp->th_sport), dstipstring, ntohs(tcp->th_dport), + tflags, (unsigned long) ntohl(tcp->th_seq), + ntohs(tcp->th_win), ntohs(tcp->th_sum), + (tcpoptinfo[0]!='\0') ? " " : "", + tcpoptinfo); + }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] ", + srcipstring, ntohs(tcp->th_sport), + dstipstring, 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); + } + + strncpy((char*)dstbuff, protoinfo, dstlen); + + return OP_SUCCESS; + +} /* End of tcppackethdrinfo() */ + + + + +int udppackethdrinfo(const u8 *packet, size_t len, u8 *dstbuff, size_t dstlen, + int detail, struct sockaddr_storage *src, struct sockaddr_storage *dst){ + + struct udp_hdr *udp = NULL; /* UDP header structure. */ + static char protoinfo[1024] = ""; /* Stores final info string. */ + struct sockaddr_in *s4=(struct sockaddr_in *)src; + struct sockaddr_in6 *s6=(struct sockaddr_in6 *)src; + struct sockaddr_in *d4=(struct sockaddr_in *)dst; + struct sockaddr_in6 *d6=(struct sockaddr_in6 *)dst; + char srcipstring[128]; + char dstipstring[128]; + + assert(packet); + assert(dstbuff); + assert(len>=8); + + udp=(struct udp_hdr *)packet; + + /* Ensure we end up with a valid detail number */ + if( detail!=LOW_DETAIL && detail!=MEDIUM_DETAIL && detail!=HIGH_DETAIL) + detail=LOW_DETAIL; + + + /* Determine target IP address */ + if(src!=NULL){ + if( s4->sin_family==AF_INET ){ + inet_ntop(AF_INET, &s4->sin_addr, srcipstring, sizeof(srcipstring)); + } + else if( s6->sin6_family==AF_INET6){ + inet_ntop(AF_INET6, &s6->sin6_addr, srcipstring, sizeof(srcipstring)); + }else{ + sprintf(dstipstring, "unknown_addr_family"); + } + }else{ + sprintf(srcipstring, "this_host"); + } + + /* Determine source IP address */ + if(dst!=NULL){ + if( d4->sin_family==AF_INET ){ + inet_ntop(AF_INET, &d4->sin_addr, dstipstring, sizeof(dstipstring)); + } + else if( d6->sin6_family==AF_INET6){ + inet_ntop(AF_INET6, &d6->sin6_addr, dstipstring, sizeof(dstipstring)); + }else{ + sprintf(dstipstring, "unknown_addr_family"); + } + }else{ + sprintf(dstipstring, "unknown_host"); + } + + if( detail == LOW_DETAIL ){ + Snprintf(protoinfo, sizeof(protoinfo), "UDP %s:%d > %s:%d", + srcipstring, ntohs(udp->uh_sport), dstipstring, ntohs(udp->uh_dport)); + }else if( detail == MEDIUM_DETAIL ){ + Snprintf(protoinfo, sizeof(protoinfo), "UDP [%s:%d > %s:%d csum=0x%04X]", + srcipstring, ntohs(udp->uh_sport), dstipstring, ntohs(udp->uh_dport), ntohs(udp->uh_sum)); + }else if( detail==HIGH_DETAIL ){ + Snprintf(protoinfo, sizeof(protoinfo), "UDP [%s:%d > %s:%d len=%d csum=0x%04X]", + srcipstring, ntohs(udp->uh_sport), dstipstring, ntohs(udp->uh_dport), + ntohs(udp->uh_ulen), ntohs(udp->uh_sum)); + } + + strncpy((char*)dstbuff, protoinfo, dstlen); + + return OP_SUCCESS; + +} /* End of udppackethdrinfo() */ + + + +/** Returns a random (null-terminated) ASCII string with no special + * meaning. Returned string may be between 1 and 512 bytes and contain + * random letters and some whitespace. + * @warning Returned string is stored in a static buffer that subsequent + * calls will overwrite. + * Note that the entropy of the returned data is very low (returned + * values are always formed by lowercase letters and whitespace). */ +const char *getRandomTextPayload(){ + int len=0, i=0; + static char buffer[512+1]; + const char letters[26]={'a','b','c','d','e','f','g','h','i','j','k', + 'l','m','n','o','p','q','r','s','t','u','v', + 'w','z','y','z'}; + + /* Determine how long the text should be */ + while( (len=(2*get_random_u8())-1) == 0 ); + /* Create the string */ + for(i=0; i<len; i++){ + if( get_random_u8()%5==0 ) + buffer[i] = ' '; // Whitespace + else + buffer[i] = letters[ get_random_u8()%26 ]; + } + buffer[len]='\0'; + return buffer; +} /* End of getRandomTextPayload() */ + + + +/** UNIMPLEMENTED */ +int send_packet(NpingTarget *target, int rawfd, u8 *pkt, size_t pktLen){ + int res; + struct sockaddr_in6 s6; + assert(pkt); + assert(target); + assert(pktLen > 0); + + if ( o.sendEth() ){ + eth_t *ethsd = eth_open_cached(o.getDevice()); + eth_send(ethsd, pkt, pktLen); + }else{ + if( o.ipv6() ){ /* IPv6 */ + memset(&s6, 0, sizeof(struct sockaddr_in6)); + s6.sin6_family=AF_INET6; + s6.sin6_addr = target->getIPv6Address(); + + /* + if( o.getMode()==TCP ){ + dport=getDstPortFromTCPHeader(pkt, pktLen); + if(dport!=NULL) + s6.sin6_port = *dport; + else + nping_fatal(QT_3, "send_packet(): Could not determine TCP destination port."); + } + else if( o.getMode()==UDP){ + dport=getDstPortFromUDPHeader(pkt, pktLen); + if(dport!=NULL) + s6.sin6_port = *dport; + else + nping_fatal(QT_3, "send_packet(): Could not determine UDP destination port."); + } + */ + + /* Linux doesn't seem to like sin6_port to be set to other value + * than 0. Unless we set it to zero, the sendto() call returns + * "Invalid argument" error. Does this happen in other systems? + * TODO: Should we check here if #ifdef LINUX and set the port to + * zero? */ + s6.sin6_port=0; + + res = Sendto("send_packet", rawfd, pkt, pktLen, 0, (struct sockaddr *)&s6, (int) sizeof(struct sockaddr_in6)); + /*Sendto returns errors as -1 according to netutil.cc so lets catch that and return OP_FAILURE*/ + if (res == -1) return OP_FAILURE; + }else{ /* IPv4 */ + struct sockaddr_storage dst; + size_t dstlen; + + dstlen = sizeof(dst); + target->getTargetSockAddr(&dst, &dstlen); + assert(dst.ss_family == AF_INET); + if( o.issetMTU() == true ) + res = send_frag_ip_packet(rawfd, NULL, (struct sockaddr_in *) &dst, pkt, pktLen, o.getMTU() ); + else + res = send_ip_packet_sd(rawfd, (struct sockaddr_in *) &dst, pkt, pktLen); + /*send_ip_packet_sd calls Sendto which returns errors as -1 according to netutil.cc so lets catch that and return OP_FAILURE*/ + if (res == -1) return OP_FAILURE; + } + } + return OP_SUCCESS; +} /* End of send_packet() */ + + + +int print_dnet_interface(const struct intf_entry *entry, void *arg) { + if (entry==NULL) + return 0; + printf("*************************************************\n"); + printf("intf_len = %d\n", entry->intf_len); + printf("intf_name = %s\n", entry->intf_name); + printf("intf_type = %u\n", entry->intf_type); + printf("intf_flags = %02x\n", entry->intf_flags); + printf("intf_mtu = %d\n", entry->intf_mtu); + printf("intf_addr = %s\n", addr_ntoa(&entry->intf_addr)); + printf("intf_dst_addr = %s\n", addr_ntoa(&entry->intf_dst_addr)); + printf("intf_link_addr = %s\n", addr_ntoa(&entry->intf_link_addr)); + printf("intf_alias_num = %d\n", entry->intf_alias_num); + for(unsigned int i=0; i<entry->intf_alias_num; i++) + printf("intf_alias_addrs[%d] = %s\n", i, addr_ntoa(&entry->intf_alias_addrs[i])); + return 0; +} + + +/* Get a list of interfaces using dnet and intf_loop. */ +int print_interfaces_dnet() { + intf_t *it; + /* Initialize the interface array. */ + it = intf_open(); + if (!it) + fatal("%s: intf_open() failed. NULL descriptor", __func__); + if (intf_loop(it, print_dnet_interface, NULL) != 0) + fatal("%s: intf_loop() failed", __func__); + intf_close(it); + return 0; +} + + + +/** @warning Returns pointer to an internal static buffer */ +struct sockaddr_storage *getSrcSockAddrFromIPPacket(u8 *pkt, size_t pktLen){ + static struct sockaddr_storage ss; + struct sockaddr_in *s_ip4=(struct sockaddr_in *)&ss; + struct sockaddr_in6 *s_ip6=(struct sockaddr_in6 *)&ss; + struct ip *i4=(struct ip*)pkt; + memset(&ss, 0, sizeof(struct sockaddr_storage)); + + if(pkt==NULL || pktLen < 20) + return NULL; + + if( i4->ip_v == 4 ){ + s_ip4->sin_family=AF_INET; + memcpy(&(s_ip4->sin_addr.s_addr), pkt+12, 4); + } + else if(i4->ip_v == 6 ){ + if(pktLen<40) /* Min length of an IPv6 header: 40 bytes*/ + return NULL; + s_ip6->sin6_family=AF_INET6; + memcpy(s_ip6->sin6_addr.s6_addr, pkt+8, 16); + } + else{ + return NULL; + } + return &ss; +} /* End of getSrcSockAddrFromPacket() */ + + + + + +u8 *getUDPheaderLocation(u8 *pkt, size_t pktLen){ + struct ip *i4=(struct ip*)pkt; + if(pkt==NULL || pktLen < 40) + return NULL; + + /* Packet is IPv4 */ + if( i4->ip_v == 4 ){ + if (i4->ip_p == IPPROTO_UDP) { + if( pktLen >= ((size_t)(i4->ip_hl*4 + 8)) ) /* We have a full IP+UDP packet */ + return pkt+(i4->ip_hl*4); + } + else + return NULL; + } + /* Packet is IPv6 */ + else if(i4->ip_v == 6 ){ + if(pktLen<40 + 8 ) + return NULL; + if( pkt[6] == IPPROTO_UDP ) /* Next Header is UDP? */ + return pkt+40; + else /* Extension headers not supported, return NULL TODO: support it? */ + return NULL; + } + else{ + return NULL; + } + return NULL; +} /* End of getUDPheaderLocation */ + + +u8 *getTCPheaderLocation(u8 *pkt, size_t pktLen){ + struct ip *i4=(struct ip*)pkt; + if(pkt==NULL || pktLen < 40) + return NULL; + + /* Packet is IPv4 */ + if( i4->ip_v == 4 ){ + if (i4->ip_p == IPPROTO_TCP) { /* Next proto is TCP? */ + if( pktLen >= ((size_t)(i4->ip_hl*4 + 20)) ) /* We have a full IP+TCP packet */ + return pkt+(i4->ip_hl*4); + } + else + return NULL; + } + /* Packet is IPv6 */ + else if(i4->ip_v == 6 ){ + if(pktLen<40 + 20 ) + return NULL; + if( pkt[6] == IPPROTO_TCP ) /* Next Header is TCP? */ + return pkt+40; + else /* Extension headers not supported, return NULL TODO: support it? */ + return NULL; + } + else{ + return NULL; + } + + return NULL; + +} /* End of getTCPHeaderLocation() */ + + + + +/* Returns the IP protocol of the packet or -1 in case of failure */ +u8 getProtoFromIPPacket(u8 *pkt, size_t pktLen){ + struct ip *i4=(struct ip*)pkt; + static u8 proto; + + if(pkt==NULL || pktLen < 28) + return -1; + + /* Packet is IPv4 */ + if( i4->ip_v == 4 ){ + proto = i4->ip_p; + return proto; + } + + /* Packet is IPv6 */ + else if(i4->ip_v == 6 ){ + proto = pkt[6]; + return proto; + } + return -1; +} /* End of getProtoFromIPPacket() */ + + + +/** @warning Returns pointer to an internal static buffer + * @return pointer on success, NULL in case of failure */ +u16 *getSrcPortFromIPPacket(u8 *pkt, size_t pktLen){ + static u16 port; + u16 *pnt=NULL; + u8 *header=NULL; + + if(pkt==NULL || pktLen < 28) + return NULL; + + if((header=getTCPheaderLocation(pkt, pktLen))==NULL){ + if ((header=getUDPheaderLocation(pkt, pktLen))==NULL) + return NULL; + + } + pnt=(u16*)&(header[0]); + port= ntohs(*pnt); + return &port; +} /* End of getSrcPortFromIPPacket() */ + + +/** @warning Returns pointer to an internal static buffer + * @return pointer on success, NULL in case of failure */ +u16 *getDstPortFromIPPacket(u8 *pkt, size_t pktLen){ + static u16 port; + u16 *pnt=NULL; + u8 *header=NULL; + + if(pkt==NULL || pktLen < 28) + return NULL; + + if((header=getTCPheaderLocation(pkt, pktLen))==NULL){ + if ((header=getUDPheaderLocation(pkt, pktLen))==NULL) + return NULL; + } + pnt=(u16*)&(header[2]); + port= ntohs(*pnt); + return &port; +} /* End of getDstPortFromIPPacket() */ + + +/** @warning Returns pointer to an internal static buffer + * @return pointer on success, NULL in case of failure */ +u16 *getDstPortFromTCPHeader(u8 *pkt, size_t pktLen){ + static u16 port; + u16 *pnt=NULL; + + if(pkt==NULL || pktLen < 20) + return NULL; + pnt=(u16*)&(pkt[2]); + port= ntohs(*pnt); + return &port; +} /* End of getDstPortFromTCPHeader() */ + + +/** @warning Returns pointer to an internal static buffer + * @return pointer on success, NULL in case of failure */ +u16 *getDstPortFromUDPHeader(u8 *pkt, size_t pktLen){ + static u16 port; + u16 *pnt=NULL; + + if(pkt==NULL || pktLen < 8) + return NULL; + pnt=(u16*)&(pkt[2]); + port= ntohs(*pnt); + return &port; +} /* End of getDstPortFromUDPHeader() */ + + +int obtainRawSocket(){ + int rawipsd=0; + int protocol=0; + int one=1; + + if( o.ipv6() ){ + switch( o.getMode() ){ + + case TCP: + protocol = IPPROTO_TCP; + break; + + case UDP: + protocol = IPPROTO_UDP; + break; + + case ICMP: + protocol = IPPROTO_ICMPV6; + break; + + case ARP: + nping_warning(QT_2,"Warning: createRawSocket() should not be called in ARP mode."); + return 0; + break; + + default: + nping_fatal(QT_3, "createRawSocket(): NpingOps::getMode() does not return a valid mode. Please report this bug."); + break; + + } + if ((rawipsd = socket(AF_INET6, SOCK_RAW, protocol)) < 0 ) + nping_fatal(QT_3,"Couldn't acquire IPv6 raw socket. Are you root?"); + + }else{ + if ((rawipsd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0 ) + nping_fatal(QT_3,"Couldn't acquire IPv4 raw socket. Are you root?"); + /* Tell the kernel we are including our own IP Header (call to + * setsockopt passing option IP_HDRINCL) */ + sethdrinclude(rawipsd); + } + + /* Allow broadcast addresses */ + if (setsockopt(rawipsd, SOL_SOCKET, SO_BROADCAST, (const char *)&one, sizeof(int)) == -1) + nping_warning(QT_2,"Failed to set SO_BROADCAST on raw socket."); + + return rawipsd; +} /* End of obtainRawSocket() */ + + +/** This function parses Linux file /proc/net/if_inet6 and returns a list + of network interfaces that are configured for IPv6. + @param ifbuff should be a buffer big enough to hold info for max_ifaces + interfaces. + + Here is some info about the format of /proc/net/if_inet6, written by + Peter Bieringer and taken from: + http://tldp.org/HOWTO/Linux+IPv6-HOWTO/proc-net.html : + + # cat /proc/net/if_inet6 + 00000000000000000000000000000001 01 80 10 80 lo + +------------------------------+ ++ ++ ++ ++ ++ + | | | | | | + 1 2 3 4 5 6 + + 1. IPv6 address displayed in 32 hexadecimal chars without colons as separator + 2. Netlink device number (interface index) in hexadecimal (see “ip addr” , too) + 3. Prefix length in hexadecimal + 4. Scope value (see kernel source “ include/net/ipv6.h” and “net/ipv6/addrconf.c” for more) + 5. Interface flags (see “include/linux/rtnetlink.h” and “net/ipv6/addrconf.c” for more) + 6. Device name + + + @warning This function is NOT portable. It will only work on Linux systems + and may not work in chroot-ed environments because it needs to be able + to access /proc/net/if_inet6. + + */ +int getinterfaces_inet6_linux(if6_t *ifbuf, int max_ifaces){ + FILE *if6file=NULL; + size_t i=0, j=0; + int readlines=0; + int parsed_ifs=0; + bool badaddr=false; + bool hasifname=false; + char buffer[2048]; + char twobytes[3]; + memset(buffer, 0, sizeof(buffer)); + + if(ifbuf==NULL || max_ifaces<=0) + nping_fatal(QT_3,"getinterfaces_inet6_linux() NULL values supplied"); + + /* TODO: Do we fatal() or should we just error and return OP_FAILURE? */ + if ( !file_is_readable(PATH_PROC_IFINET6) ) + nping_fatal(QT_3, "Couldn't get IPv6 interface information. File %s does not exist or you don't have read permissions.", PATH_PROC_IFINET6); + if( (if6file=fopen(PATH_PROC_IFINET6, "r"))==NULL ) + nping_fatal(QT_3, "Failed to open %s.", PATH_PROC_IFINET6); + + while( fgets(buffer,sizeof(buffer), if6file) ){ + + if(parsed_ifs>=max_ifaces) + break; + + nping_print(DBG_4, "Read %s:%d: %s\n", PATH_PROC_IFINET6, ++readlines, buffer); + + /* Check the line has the expected format ********************************/ + /* Some versions of the kernel include colons in the IPv6 address, some + * others don't. E.g: + * fe80:0000:0000:0000:0333:a5ff:4444:9306 03 40 20 80 wlan0 + * fe800000000000000333a5ff44449306 03 40 20 80 wlan0 + * So what we do is to remove the colons so we can process the line + * no matter the format of the IPv6 addresses. + * + * TODO: Can interfaces with format eth0:1 appear on /proc/net/if_inet6? + * If they can, then we need to change the code to skip the last : */ + removecolon(buffer); + + /* 1. Check it has the correct length */ + if( strlen(buffer) < strlen("00000000000000000000000000000001 01 80 10 80 lo") ){ + continue; + } + /* 2. Check the inet6 address only contains hex digits */ + for(i=0; i<32; i++){ + if( !isxdigit(buffer[i]) ){ + badaddr=true; + break; + } + } + if(badaddr){ + badaddr=false; + continue; + } + /* 2. Check spaces are in the appropriate place */ + if( buffer[32]!=' ' || buffer[35]!=' ' || buffer[38]!=' ' || buffer[41]!=' ' || buffer[44]!=' ' ){ + continue; + } + + /* 3. Check we have numbers in the part where we are supposed to have them */ + if( !isxdigit( buffer[33] ) || !isxdigit( buffer[34] ) || + !isxdigit( buffer[36] ) || !isxdigit( buffer[37] ) || + !isxdigit( buffer[39] ) || !isxdigit( buffer[40] ) || + !isxdigit( buffer[42] ) || !isxdigit( buffer[43] ) ){ + continue; + } + + /* 4. Check we actually have an interface name afterwards */ + for(i=44; i<strlen(buffer); i++){ + if( isalpha(buffer[i]) ) + hasifname=true; + } + if(!hasifname){ + hasifname=false; + continue; + } + + /* If we get here means the read line has the expected format so we + * read the information and store it in a interface_info structure * + */ + + /* Store IPv6 address */ + u8 ipv6addr[16]; + for(i=0, j=0; j<16 && i<32; i+=2){ + twobytes[0]=buffer[i]; + twobytes[1]=buffer[i+1]; + twobytes[2]='\0'; + ipv6addr[j++]=(u8)strtol(twobytes, NULL, 16); + } + + /* Store Netlink device number */ + u8 dev_no; + twobytes[0]=buffer[33]; twobytes[1]=buffer[34]; twobytes[2]='\0'; + dev_no=(u8)strtol(twobytes, NULL, 16); + + /* Store prefix length */ + u8 prefix_len; + twobytes[0]=buffer[36]; twobytes[1]=buffer[37]; twobytes[2]='\0'; + prefix_len=(u8)strtol(twobytes, NULL, 16); + + /* Store scope value */ + u8 scope_value; + twobytes[0]=buffer[39]; twobytes[1]=buffer[40]; twobytes[2]='\0'; + scope_value=(u8)strtol(twobytes, NULL, 16); + + /* Store interface flags */ + u8 dev_flags; + twobytes[0]=buffer[42]; twobytes[1]=buffer[43]; twobytes[2]='\0'; + dev_flags=(u8)strtol(twobytes, NULL, 16); + + /* Store interface name */ + char devname[DEVNAMELEN]; + memset(devname, 0, DEVNAMELEN); + for(i=44, j=0; i<strlen(buffer) && j<DEVNAMELEN-1; i++){ + if( buffer[i]==' ' || buffer[i]=='\n') + continue; + else + devname[j++]=buffer[i]; + } + devname[j]='\0'; + + + /* Once we have all the info, copy it to user supplied buffer */ + memset(&ifbuf[parsed_ifs], 0, sizeof(if6_t)); + memcpy( ifbuf[parsed_ifs].devname, devname, DEVNAMELEN); + struct sockaddr_in6 *s6=(struct sockaddr_in6 *)&ifbuf[parsed_ifs].ss; + s6->sin6_family=AF_INET6; + memcpy(s6->sin6_addr.s6_addr, ipv6addr, 16); + memcpy(ifbuf[parsed_ifs].addr, ipv6addr, 16); + ifbuf[parsed_ifs].netmask_bits=prefix_len; + ifbuf[parsed_ifs].dev_no=dev_no; + ifbuf[parsed_ifs].scope=scope_value; + ifbuf[parsed_ifs].flags=dev_flags; + /* ifbuf[parsed_ifs].mac = ??? (we don't know, we don't set it) */ + + parsed_ifs++; + +/* Debugging code: This should print the exact same lines that + * /proc/net/if_inet6 contains. (well, unless that kernel includes colons + * in the ipv6 address) + * + for(i=0; i<16; i++) + printf("%02x", ipv6addr[i]); + printf(" %02x", dev_no); + printf(" %02x", prefix_len); + printf(" %02x", scope_value); + printf(" %02x", dev_flags); + printf(" %8s\n", devname); + */ + + } /* End of loop */ + + /* Cleanup */ + if(if6file) + fclose(if6file); + return parsed_ifs; +} /* End of getinterfaces_inet6_linux() */ + + +/** This function parses Linux file /proc/net/ipv6_route and returns a list + of routes for IPv6 packets. + @param ifbuff should be a buffer big enough to hold info for max_routes + routes. + + Here is some info about the format of /proc/net/if_inet6, written by + Peter Bieringer and taken from: + http://tldp.org/HOWTO/Linux+IPv6-HOWTO/proc-net.html : + + # cat /proc/net/ipv6_route + 00000000000000000000000000000000 00 00000000000000000000000000000000 00 + +------------------------------+ ++ +------------------------------+ ++ + | | | | + 1 2 3 4 + + ¬ 00000000000000000000000000000000 ffffffff 00000001 00000001 00200200 lo + ¬ +------------------------------+ +------+ +------+ +------+ +------+ ++ + ¬ | | | | | | + ¬ 5 6 7 8 9 10 + + 1. IPv6 destination network displayed in 32 hexadecimal chars without colons as separator + 2. IPv6 destination prefix length in hexadecimal + 3. IPv6 source network displayed in 32 hexadecimal chars without colons as separator + 4. IPv6 source prefix length in hexadecimal + 5. IPv6 next hop displayed in 32 hexadecimal chars without colons as separator + 6. Metric in hexadecimal + 7. Reference counter + 8. Use counter + 9. Flags +10. Device name + + @warning This function is NOT portable. It will only work on Linux systems + and may not work in chroot-ed environments because it needs to be able + to access /proc/net/ipv6_route. + */ +int getroutes_inet6_linux(route6_t *rtbuf, int max_routes){ + FILE *route6file=NULL; + size_t i=0, j=0; + int readlines=0; + int parsed_routes=0; + bool badchars=false; + bool hasifname=false; + char buffer[2048]; + char twobytes[3]; + memset(buffer, 0, sizeof(buffer)); + + if(rtbuf==NULL || max_routes<=0) + nping_fatal(QT_3,"getroutes_inet6_linux() NULL values supplied"); + + /* TODO: Do we fatal() or should we just error and return OP_FAILURE? */ + if ( !file_is_readable(PATH_PROC_IPV6ROUTE) ) + nping_fatal(QT_3, "Couldn't get IPv6 route information. File %s does not exist or you don't have read permissions.", PATH_PROC_IPV6ROUTE); + if( (route6file=fopen(PATH_PROC_IPV6ROUTE, "r"))==NULL ) + nping_fatal(QT_3, "Failed to open %s.", PATH_PROC_IPV6ROUTE); + + while( fgets(buffer,sizeof(buffer), route6file) ){ + + if(parsed_routes>=max_routes) + break; + + nping_print(DBG_4, "Read %s:%d: %s\n",PATH_PROC_IPV6ROUTE, ++readlines, buffer); + + /* Check the line has the expected format ********************************/ + /* Some versions of the kernel include colons in the IPv6 address, some + * others don't. So what we do is to remove the colons so we can process + * the line no matter the format of the IPv6 addresses. + * + * TODO: Can interfaces with format eth0:1 appear on /proc/net/ipv6_route? + * If they can, then we need to change the code to skip the last : */ + removecolon(buffer); + + /* 1. Check it has the correct length. */ + size_t min_len=0; + min_len += 3*32; /* Three IPv6 addresses in hex */ + min_len += 2*2; /* Two 8bit hex values (prefix lengths) */ + min_len += 4*8; /* Four 32-bit hex values */ + min_len += 1; /* I guess one char is the min for a device len */ + min_len += 9; /* 9 spaces */ + if( strlen(buffer) < min_len ){ + continue; + } + /* 2. Check the first 140 characters only contain hex digits or spaces */ + for(i=0; i<140; i++){ + if( !isxdigit(buffer[i]) && buffer[i]!=' '){ + badchars=true; + break; + } + } + if(badchars){ + badchars=false; + continue; + } + /* 2. Check spaces are in the appropriate place */ + if( buffer[32]!=' ' || buffer[71]!=' ' || buffer[122]!=' ' || + buffer[35]!=' ' || buffer[104]!=' ' || buffer[131]!=' ' || + buffer[68]!=' ' || buffer[113]!=' ' || buffer[140]!=' ' ){ + continue; + } + + /* 4. Check we actually have an interface name afterwards */ + for(i=140; i<strlen(buffer); i++){ + if( isalpha(buffer[i]) ) + hasifname=true; + } + if(!hasifname){ + hasifname=false; + continue; + } + + /* If we get here means the read line has the expected format so we + * read the information and store it in a interface_info structure * + */ + + /* Store destination network address */ + u8 dst_addr[16]; + for(i=0, j=0; j<16 && i<32; i+=2){ + twobytes[0]=buffer[i]; twobytes[1]=buffer[i+1]; twobytes[2]='\0'; + dst_addr[j++]=(u8)strtol(twobytes, NULL, 16); + } + /* Store destination network prefix */ + u8 dst_prefix; + twobytes[0]=buffer[33]; twobytes[1]=buffer[34]; twobytes[2]='\0'; + dst_prefix=(u8)strtol(twobytes, NULL, 16); + + /* Store source network address */ + u8 src_addr[16]; + for(i=36, j=0; j<16 && i<68; i+=2){ + twobytes[0]=buffer[i]; twobytes[1]=buffer[i+1]; twobytes[2]='\0'; + src_addr[j++]=(u8)strtol(twobytes, NULL, 16); + } + /* Store source network prefix */ + u8 src_prefix; + twobytes[0]=buffer[69]; twobytes[1]=buffer[70]; twobytes[2]='\0'; + src_prefix=(u8)strtol(twobytes, NULL, 16); + + /* Store next hop address */ + u8 nh_addr[16]; + for(i=72, j=0; j<16 && i<104; i+=2){ + twobytes[0]=buffer[i]; twobytes[1]=buffer[i+1]; twobytes[2]='\0'; + nh_addr[j++]=(u8)strtol(twobytes, NULL, 16); + } + + /* Store metric */ + u8 metric[4]; + for(i=105, j=0; j<4 && i<113; i+=2){ + twobytes[0]=buffer[i]; twobytes[1]=buffer[i+1]; twobytes[2]='\0'; + metric[j++]=(u8)strtol(twobytes, NULL, 16); + } + + /* Store reference counter */ + u8 ref_count[4]; + for(i=114, j=0; j<4 && i<122; i+=2){ + twobytes[0]=buffer[i]; twobytes[1]=buffer[i+1]; twobytes[2]='\0'; + ref_count[j++]=(u8)strtol(twobytes, NULL, 16); + } + + /* Store use counter */ + u8 use_count[4]; + for(i=123, j=0; j<4 && i<131; i+=2){ + twobytes[0]=buffer[i]; twobytes[1]=buffer[i+1]; twobytes[2]='\0'; + use_count[j++]=(u8)strtol(twobytes, NULL, 16); + } + + /* Store flags */ + u8 flags[4]; + for(i=132, j=0; j<4 && i<140; i+=2){ + twobytes[0]=buffer[i]; twobytes[1]=buffer[i+1]; twobytes[2]='\0'; + flags[j++]=(u8)strtol(twobytes, NULL, 16); + } + + /* Store interface name */ + char devname[DEVNAMELEN]; + memset(devname, 0, DEVNAMELEN); + for(i=140, j=0; i<strlen(buffer) && j<DEVNAMELEN-1; i++){ + if( buffer[i]==' ' || buffer[i]=='\n') + continue; + else + devname[j++]=buffer[i]; + } + devname[j]='\0'; + + /* Once we have all the info, copy it to user supplied buffer */ + memset(&rtbuf[parsed_routes], 0, sizeof(route6_t)); + memcpy(rtbuf[parsed_routes].dst_net.s6_addr, dst_addr, 16); + rtbuf[parsed_routes].dst_prefix=dst_prefix; + memcpy(rtbuf[parsed_routes].src_net.s6_addr, src_addr, 16); + rtbuf[parsed_routes].src_prefix=src_prefix; + memcpy(rtbuf[parsed_routes].next_hop.s6_addr, nh_addr, 16); + + /* TODO: Check the endianness stuff here is implemented right. + * The thing is that the part of the linux kernel that prints the info + * to /proc/net/ipv6_rout is the following: + * [From /net/ipv6/route.c ] + * 2427 seq_printf(m, " %08x %08x %08x %08x %8s\n", + * 2428 rt->rt6i_metric, atomic_read(&rt->u.dst.__refcnt), + * 2429 rt->u.dst.__use, rt->rt6i_flags, + * 2430 rt->rt6i_dev ? rt->rt6i_dev->name : ""); + * + * So as they are actually printing 32bit values with %08x, they are + * getting printed out in network byte order (big endian) so we call + * ntohl() for each of them so we actually convert them to the right + * representation in the current machine. With 8-bit values we have no + * problem because they are converted to binary using strtol() and it + * handles endianness by itself. Am I doing anything wrong here? + * */ + memcpy(&rtbuf[parsed_routes].metric, metric, 4); + rtbuf[parsed_routes].metric=ntohl(rtbuf[parsed_routes].metric); + memcpy(&rtbuf[parsed_routes].ref_count, ref_count, 4); + rtbuf[parsed_routes].ref_count=ntohl(rtbuf[parsed_routes].ref_count); + memcpy(&rtbuf[parsed_routes].use_count, use_count, 4); + rtbuf[parsed_routes].use_count=ntohl(rtbuf[parsed_routes].use_count); + memcpy(&rtbuf[parsed_routes].flags, flags, 4); + rtbuf[parsed_routes].flags=ntohl(rtbuf[parsed_routes].flags); + memcpy(rtbuf[parsed_routes].devname, devname, DEVNAMELEN); + +/* Debugging code: This should print the exact same lines that + * /proc/net/if_inet6 contains. (well, unless that kernel includes colons + * in the ipv6 address) + * + for(i=0; i<16; i++) + printf("%02x", rtbuf[parsed_routes].dst_net.s6_addr[i]); + + printf(" %02x ", rtbuf[parsed_routes].dst_prefix); + + for(i=0; i<16; i++) + printf("%02x", rtbuf[parsed_routes].src_net.s6_addr[i]); + + printf(" %02x ", rtbuf[parsed_routes].src_prefix); + + for(i=0; i<16; i++) + printf("%02x", rtbuf[parsed_routes].next_hop.s6_addr[i]); + + printf(" %08x", rtbuf[parsed_routes].metric); + printf(" %08x", rtbuf[parsed_routes].ref_count); + printf(" %08x", rtbuf[parsed_routes].use_count); + printf(" %08x", rtbuf[parsed_routes].flags); + printf(" %8s\n", rtbuf[parsed_routes].devname); +*/ + parsed_routes++; + + } /* End of loop */ + + /* Cleanup */ + if(route6file) + fclose(route6file); + return parsed_routes; +} /* End of getroutes_inet6_linux() */ + + +/** This function takes a sockaddr_storage pointer that MUST contain a valid + * IPv6 address (a sockaddr_in6 struct with sin6_family set to AF_INET6), + * and returns the best route entry for the supplied destination. + * The route entries are read from /proc/net/ipv6_route through function + * getroutes_inet6_linux(). + * @warning This function is NOT portable. It will only work on Linux systems + * and may not work in chroot-ed environments because it needs to be able + * to access /proc/net/ipv6_route. + * @warning It returns NULL in case of error. Check for it or you'll segfault. + * @warning returned pointer points to a static buffer that subsequent calls + * will overwrite. */ +route6_t *route_dst_ipv6_linux(const struct sockaddr_storage *const dst){ + struct sockaddr_in6 *dstsin6=NULL; /* Cast for supplied sockaddr_storage var */ + route6_t routes6[64]; /* Array of IPv6 routes */ + int total_routes6=0; /* Number of returned routes */ + static route6_t theone; /* Stores the best route we find */ + route6_t *def_gw=NULL; /* Stores default gateway */ + int best_match=0; /* Max number of bits that we've matched */ + int curr_match=0; /* Matching bits in current route */ + u8 zero_addr[16]; /* Just to compare route to addr "::" */ + memset(zero_addr, 0, 16); + dstsin6=(struct sockaddr_in6 *)dst; + + if(dst==NULL) return NULL; + if(dstsin6->sin6_family!=AF_INET6) return NULL; + + /* Let's parse /proc/net/ipv6_route and get a list of routes */ + if ( (total_routes6=getroutes_inet6_linux(routes6, 64)) <= 0 ) + return NULL; + + /* Now we go over the whole route list and select the match that has the + * largest prefix length */ + + for(int i=0; i<total_routes6; i++){ + /* Check how many bits they have in common */ + curr_match=bitcmp(dstsin6->sin6_addr.s6_addr, routes6[i].dst_net.s6_addr, 16); + + /* Select only the best match (always taking into account that + * our dst address needs to match at least dst_prefix bits. */ + if( curr_match > best_match && curr_match>=routes6[i].dst_prefix){ + best_match=curr_match; + memcpy(&theone, &routes6[i], sizeof(route6_t)); + } + /* There was no match, but we check if the route is actually "::" + * (like 0.0.0.0 in IPv4). If it is, we store it, just in case we + * end up without a better route. */ + else if ( !memcmp( routes6[i].dst_net.s6_addr, zero_addr, 16) ){ + if(def_gw==NULL){ + def_gw=&routes6[i]; + } + else if( !strncmp("lo", def_gw->devname, 2) ){ + /* If the route we have is through the loopback interface, + * overwrite it, we prefer to choose any other device as + * the default gateway. We just compare the first two + * letters cause in Linux the interface is called "lo" but + * on BSD is usually called lo0. */ + def_gw=&routes6[i]; + } + } + } + if( best_match==0 ){ + if(def_gw!=NULL) + memcpy(&theone, def_gw, sizeof(route6_t)); + else return NULL; + } + return &theone; +} /* End of route_dst_ipv6() */ |