/* * $Id: nemesis-functions.c,v 1.2 2004/10/07 01:20:56 jnathan Exp $ * * THE NEMESIS PROJECT * Copyright (C) 2002, 2003 Jeff Nathan * * nemesis-functions.c (nemesis utility functions) * */ #if defined(HAVE_CONFIG_H) #include "config.h" #endif #include #include #include #if defined(TIME_WITH_SYS_TIME) || defined(WIN32) #include #include #else #if defined(HAVE_SYS_TIME_H) #include #elif defined(HAVE_TIME_H) #include #endif #endif #if defined(WIN32) #include #endif #include #if defined(HAVE_LIMITS_H) || defined(WIN32) #include #endif #if defined(HAVE_NETDB_H) #include #endif #include #if defined(HAVE_ERRNO_H) || defined(WIN32) #include #endif #if defined(HAVE_NETINET_IN_H) #include #elif defined(WIN32) #include #include #endif #include #include "nemesis.h" const char *version = " -=- The NEMESIS Project Version 1.4"; char zero[ETHER_ADDR_LEN]; char one[ETHER_ADDR_LEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; char title[TITLEBUFFSIZE]; char errbuf[ERRBUFFSIZE]; /* all-purpose error buffer */ char *validtcpflags = "FSRPAUEC-"; /* TCP flag index */ int verbose; /* verbosity */ int got_link; int got_ipoptions; int got_tcpoptions; /** * Convert user supplied string to a u_int32_t or exit on invalid data. * * @param str string to be converted * * @returns u_int32_t conversion of input string */ u_int32_t xgetint32(const char *str) { char *endp; u_long val; val = strtoul(str, &endp, 0); if (val > UINT_MAX || str == endp || *endp) { fprintf(stderr, "ERROR: Argument %s must be a positive integer between " "0 and %u.\n", str, UINT_MAX); exit(1); } else return (u_int32_t)val; } /** * Convert user supplied string to a u_int16_t or exit on invalid data. * * @param str string to be converted * * @return u_int16_t conversion of input string */ u_int16_t xgetint16(const char *str) { char *endp; u_long val; val = strtoul(str, &endp, 0); if (val > USHRT_MAX || str == endp || *endp) { fprintf(stderr, "ERROR: Argument %s must be a positive integer between " "0 and %hu.\n", str, USHRT_MAX); exit(1); } else return (u_int16_t)val; } /** * Convert user supplied string to a u_int8_t or exit on invalid data. * * @param str string to be converted * * @return u_int8_t conversion of input string */ u_int8_t xgetint8(const char *str) { char *endp; u_long val; val = strtoul(str, &endp, 0); if (val > UCHAR_MAX || str == endp || *endp) { fprintf(stderr, "ERROR: Argument %s must be a positive integer between " "0 and %u.\n", str, UCHAR_MAX); exit(1); } else return (u_int8_t)val; } /** * Parses a string to set the fragmentation options in an IP header * * @param iph pointer to an IPhdr structure * @param str string to be parsed * * @note Optimized by Marty Roesch . * * @return 0 on sucess, -1 on failure **/ int parsefragoptions(IPhdr *iph, char *str) { int reserved = 0, dont = 0, more = 0, offset = 0; int i, argcount = 0; u_int8_t error = 0; char *orig = NULL; /* original input string */ char *toks[FP_MAX_ARGS]; /* break all args down into option sets */ char **ap; u_int16_t frag_offset = 0; orig = strdup(str); for (ap = toks; ap < &toks[FP_MAX_ARGS] && (*ap = strsep(&str, " ,")) != NULL; ) { if (**ap != '\0') { ap++; argcount++; } } *ap = NULL; for (i = 0; i < argcount; i++) { if (toks[i][0] == 'D') { if (!dont) dont++; else { error++; break; } } else if (toks[i][0] == 'M') { if (!more) more++; else { error++; break; } } else if (toks[i][0] == 'R') { if (!reserved) reserved++; else { error++; break; } } else if (isdigit((int)toks[i][0])) { if (!offset) { offset++; frag_offset = xgetint16(toks[i]); } else { error++; break; } } else { error++; break; } } if (error > 0) { fprintf(stderr, "ERROR: Invalid IP fragmentation options " "specification: %s.\n", orig); if (orig != NULL) free(orig); return -1; } if (frag_offset > 8189) { fprintf(stderr, "ERROR: Fragmentation offset %hu must be a positive " "integer between 0 and 8189.\n", frag_offset); if (orig != NULL) free(orig); return -1; } iph->ip_off = (frag_offset & IP_OFFMASK) | ((reserved == 1 ? IP_RF : 0) | (dont == 1 ? IP_DF : 0) | (more == 1 ? IP_MF : 0)); if (orig != NULL) free(orig); return 0; } /** * * Convert a hostname or IP address, supplied in ASCII format, to an u_int32_t * in network byte order. * * @param hostname host name or IP address in ASCII * @param address u_int32_t pointer to hold converted IP * * @return 0 on sucess, -1 on failure */ int nemesis_name_resolve(char *hostname, u_int32_t *address) { struct in_addr saddr; struct hostent *hp = NULL; #if !defined(WIN32) extern int h_errno; #else TCHAR WinErrBuf[1000]; #endif if (address == NULL || hostname == NULL) return -1; if ((inet_aton(hostname, &saddr)) < 1) { if ((hp = gethostbyname(hostname)) == NULL) { #if !defined(WIN32) fprintf(stderr, "ERROR: Unable to resolve supplied hostname: " "%s. %s\n", hostname, hstrerror(h_errno)); #else if (winstrerror(WinErrBuf, sizeof(WinErrBuf)) < 0) return -1; else { fprintf(stderr, "ERROR: Unable to resolve supplied hostname: " "%s.\n%s\n", hostname, WinErrBuf); } #endif return -1; } /* Do not blindly disregard the size of the address returned */ if (hp->h_length != 4) { fprintf(stderr, "ERROR: nemesis_name_resolve() received a non IPv4 " "address.\n"); return -1; } memcpy((u_int32_t *)address, hp->h_addr, 4); return 0; } else { if (!memcmp(&saddr.s_addr, zero, 4)) return -1; memcpy((u_int32_t *)address, &saddr.s_addr, 4); return 0; } } /** * Determine if a source Ethernet address has been specified and fill in the * ETHERhdr structure if necessary. * * @param eth ETHERhdr pointer containing the source Ethernet address * @param device char pointer containing the Ethernet device name * * @return 0 on sucess, -1 on failure */ int nemesis_check_link(ETHERhdr *eth, char *device) { int i; struct ether_addr *e = NULL; struct libnet_link_int l2; memset(&l2, 0, sizeof(struct libnet_link_int)); #ifdef DEBUG printf("DEBUG: determining if device %s\n has a hardware address " "assigned.\n", device); #endif if (!memcmp(eth->ether_shost, zero, 6)) { memset(&l2, 0, sizeof(l2)); if ((e = libnet_get_hwaddr(&l2, device, errbuf)) == NULL) return -1; for (i = 0; i < 6; i++) eth->ether_shost[i] = e->ether_addr_octet[i]; return 0; } else return 0; } /** * Lookup and return the string associated with each link type. * * @param linktype integer represntation of linktype * * @return char * containing the appropriate linktype or Unknown on a failed * match. */ char *nemesis_lookup_linktype(int linktype) { char *dlt; switch(linktype) { case 0: dlt = "DLT_NULL"; break; case 1: dlt = "DLT_EN10MB"; break; case 2: dlt = "DLT_EN3MB"; break; case 3: dlt = "DLT_AX25"; break; case 4: dlt = "DLT_PRONET"; break; case 5: dlt = "DLT_CHAOS"; break; case 6: dlt = "DLT_IEEE802"; break; case 7: dlt = "DLT_ARCNET"; break; case 8: dlt = "DLT_SLIP"; break; case 9: dlt = "DLT_PPP"; break; case 10: dlt = "DLT_FDDI"; break; case 11: dlt = "DLT_ATM_RFC1483"; break; case 12: dlt = "DLT_LOOP"; break; case 13: dlt = "DLT_ENC"; break; case 14: dlt = "DLT_RAW"; break; case 15: dlt = "DLT_SLIP_BSDOS"; break; case 16: dlt = "DLT_PPP_BSDOS"; break; default: dlt = "UNKNOWN"; } return dlt; } /** * Seed the random number generator * * @return 0 on success, -1 on failure */ int nemesis_seedrand(void) { #if !defined(WIN32) extern int errno; struct timeval tv; if (gettimeofday(&tv, NULL) == -1) { fprintf(stderr, "ERROR: nemesis_seedrand() failed in gettimeofday(): " "%s.\n", strerror(errno)); return -1; } srandom((unsigned int)tv.tv_usec ^ (unsigned int)fabs(fmod(time(NULL), UINT_MAX))); #endif return 0; } #if 0 /** * Figures out how to adjust the current clock reading based on the timezone * you're in. Ripped off from TCPdump. * * @param time_t offset from GMT * * @return offset seconds from GMT */ int gmt2local(time_t t) { register int dt, dir; register struct tm *gmt, *loc; struct tm sgmt; if (t == 0) t = time(NULL); gmt = &sgmt; *gmt = *gmtime(&t); loc = localtime(&t); dt = (loc->tm_hour - gmt->tm_hour) * 60 * 60 + (loc->tm_min - gmt->tm_min) * 60; dir = loc->tm_year - gmt->tm_year; if (dir == 0) dir = loc->tm_yday - gmt->tm_yday; dt += dir * 24 * 60 * 60; return(dt); } #endif #if defined(WIN32) /** * Lookup Windows system errors and copy them into a user supplied buffer. * * @param str Buffer to hold the error string. * @param size Size of the error buffer. * * @return void function. */ int winstrerror(LPSTR str, int size) { LPVOID lpMsgBuf; if (str != NULL && size > 2) { FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&lpMsgBuf, 0, NULL); strlcpy(str, (LPCTSTR)lpMsgBuf, size); LocalFree(lpMsgBuf); lpMsgBuf = NULL; return 0; } else { fprintf(stderr, "ERROR: winstrerror() received NULL buffer or " "buffer length < 2.\n"); return -1; } } /** * * Lookup the Windows device name from the list of network devices and return * the proper device at the user-supplied list index. * * @param devnum Device number. * * @return 0 on sucess, -1 on failure */ int nemesis_getdev(int devnum, char **device) { char *lookuptmp = NULL; if (devnum > 0) { if ((lookuptmp = pcap_lookupdev(errbuf)) == NULL) { fprintf(stderr, "ERROR: Unable to allocate device memory: %s." "\n", errbuf); return -1; } else { device = GetAdapterFromList(lookuptmp, devnum); return 0; } } else { fprintf(stderr,"ERROR: Invalid interface: '%d'.\n", devnum); return -1; } } #endif /* WIN32 */