text: /*
   * $Id: nemesis-functions.c,v 1.2 2004/10/07 01:20:56 jnathan Exp $
   *
   * THE NEMESIS PROJECT
   * Copyright (C) 2002, 2003 Jeff Nathan <jeff@snort.org>
   *
   * nemesis-functions.c (nemesis utility functions)
   *
   */
  
  #if defined(HAVE_CONFIG_H)
      #include "config.h"
  #endif
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #if defined(TIME_WITH_SYS_TIME) || defined(WIN32)
      #include <sys/time.h>
      #include <time.h>
  #else
      #if defined(HAVE_SYS_TIME_H)
          #include <sys/time.h>
      #elif defined(HAVE_TIME_H)
          #include <time.h>
      #endif
  #endif
  #if defined(WIN32)
      #include <pcap.h>
  #endif
  #include <unistd.h>
  #if defined(HAVE_LIMITS_H) || defined(WIN32)
      #include <limits.h>
  #endif
  #if defined(HAVE_NETDB_H)
      #include <netdb.h>
  #endif
  #include <math.h>
  #if defined(HAVE_ERRNO_H) || defined(WIN32)
      #include <errno.h>
  #endif
  #if defined(HAVE_NETINET_IN_H)
      #include <netinet/in.h>
  #elif defined(WIN32)
      #include <winsock2.h>
      #include <process.h>
  #endif
  #include <libnet.h>
  #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 <roesch@sourcefire.com>.
   *
   * @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 */
  