Diff for /embedaddon/dnsmasq/src/util.c between versions 1.1.1.1 and 1.1.1.4

version 1.1.1.1, 2013/07/29 19:37:40 version 1.1.1.4, 2021/03/17 00:56:46
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
   
    This program is free software; you can redistribute it and/or modify     This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by     it under the terms of the GNU General Public License as published by
Line 24 Line 24
 #include <sys/times.h>  #include <sys/times.h>
 #endif  #endif
   
#if defined(LOCALEDIR) || defined(HAVE_IDN)#if defined(HAVE_LIBIDN2)
 #include <idn2.h>
 #elif defined(HAVE_IDN)
 #include <idna.h>  #include <idna.h>
 #endif  #endif
   
#ifdef HAVE_ARC4RANDOM#ifdef HAVE_LINUX_NETWORK
void rand_init(void)#include <sys/utsname.h>
{#endif
  return; 
} 
   
 unsigned short rand16(void)  
 {  
    return (unsigned short) (arc4random() >> 15);  
 }  
   
 #else  
   
 /* SURF random number generator */  /* SURF random number generator */
   
 static u32 seed[32];  static u32 seed[32];
 static u32 in[12];  static u32 in[12];
 static u32 out[8];  static u32 out[8];
   static int outleft = 0;
   
 void rand_init()  void rand_init()
 {  {
Line 83  static void surf(void) Line 77  static void surf(void)
   
 unsigned short rand16(void)  unsigned short rand16(void)
 {  {
     if (!outleft) 
       {
         if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3];
         surf();
         outleft = 8;
       }
     
     return (unsigned short) out[--outleft];
   }
   
   u32 rand32(void)
   {
    if (!outleft) 
       {
         if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3];
         surf();
         outleft = 8;
       }
     
     return out[--outleft]; 
   }
   
   u64 rand64(void)
   {
   static int outleft = 0;    static int outleft = 0;
   
  if (!outleft) {  if (outleft < 2)
    if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3];    {
    surf();      if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3];
    outleft = 8;      surf();
  }      outleft = 8;
     }
   
   outleft -= 2;
   
  return (unsigned short) out[--outleft];  return (u64)out[outleft+1] + (((u64)out[outleft]) << 32);
 }  }
   
#endif/* returns 2 if names is OK but contains one or more underscores */
 
 static int check_name(char *in)  static int check_name(char *in)
 {  {
   /* remove trailing .     /* remove trailing . 
Line 103  static int check_name(char *in) Line 123  static int check_name(char *in)
   size_t dotgap = 0, l = strlen(in);    size_t dotgap = 0, l = strlen(in);
   char c;    char c;
   int nowhite = 0;    int nowhite = 0;
     int hasuscore = 0;
       
   if (l == 0 || l > MAXDNAME) return 0;    if (l == 0 || l > MAXDNAME) return 0;
       
   if (in[l-1] == '.')    if (in[l-1] == '.')
     {      {
       if (l == 1) return 0;  
       in[l-1] = 0;        in[l-1] = 0;
         nowhite = 1;
     }      }
  
   for (; (c = *in); in++)    for (; (c = *in); in++)
     {      {
       if (c == '.')        if (c == '.')
Line 121  static int check_name(char *in) Line 142  static int check_name(char *in)
       else if (isascii((unsigned char)c) && iscntrl((unsigned char)c))         else if (isascii((unsigned char)c) && iscntrl((unsigned char)c)) 
         /* iscntrl only gives expected results for ascii */          /* iscntrl only gives expected results for ascii */
         return 0;          return 0;
#if !defined(LOCALEDIR) && !defined(HAVE_IDN)#if !defined(HAVE_IDN) && !defined(HAVE_LIBIDN2)
       else if (!isascii((unsigned char)c))        else if (!isascii((unsigned char)c))
         return 0;          return 0;
 #endif  #endif
       else if (c != ' ')        else if (c != ' ')
        nowhite = 1;        {
           nowhite = 1;
           if (c == '_')
             hasuscore = 1;
         }
     }      }
   
   if (!nowhite)    if (!nowhite)
     return 0;      return 0;
   
  return 1;  return hasuscore ? 2 : 1;
 }  }
   
 /* Hostnames have a more limited valid charset than domain names  /* Hostnames have a more limited valid charset than domain names
Line 142  static int check_name(char *in) Line 167  static int check_name(char *in)
 int legal_hostname(char *name)  int legal_hostname(char *name)
 {  {
   char c;    char c;
     int first;
   
   if (!check_name(name))    if (!check_name(name))
     return 0;      return 0;
   
  for (; (c = *name); name++)  for (first = 1; (c = *name); name++, first = 0)
     /* check for legal char a-z A-Z 0-9 - _ . */      /* check for legal char a-z A-Z 0-9 - _ . */
     {      {
       if ((c >= 'A' && c <= 'Z') ||        if ((c >= 'A' && c <= 'Z') ||
           (c >= 'a' && c <= 'z') ||            (c >= 'a' && c <= 'z') ||
          (c >= '0' && c <= '9') ||          (c >= '0' && c <= '9'))
          c == '-' || c == '_') 
         continue;          continue;
   
         if (!first && (c == '-' || c == '_'))
           continue;
               
       /* end of hostname part */        /* end of hostname part */
       if (c == '.')        if (c == '.')
Line 168  int legal_hostname(char *name) Line 196  int legal_hostname(char *name)
 char *canonicalise(char *in, int *nomem)  char *canonicalise(char *in, int *nomem)
 {  {
   char *ret = NULL;    char *ret = NULL;
 #if defined(LOCALEDIR) || defined(HAVE_IDN)  
   int rc;    int rc;
#endif  
 
   if (nomem)    if (nomem)
     *nomem = 0;      *nomem = 0;
       
  if (!check_name(in))  if (!(rc = check_name(in)))
     return NULL;      return NULL;
       
#if defined(LOCALEDIR) || defined(HAVE_IDN)#if defined(HAVE_LIBIDN2) && (!defined(IDN2_VERSION_NUMBER) || IDN2_VERSION_NUMBER < 0x02000003)
  if ((rc = idna_to_ascii_lz(in, &ret, 0)) != IDNA_SUCCESS)  /* older libidn2 strips underscores, so don't do IDN processing
      if the name has an underscore (check_name() returned 2) */
   if (rc != 2)
 #endif
 #if defined(HAVE_IDN) || defined(HAVE_LIBIDN2)
     {      {
      if (ret)#  ifdef HAVE_LIBIDN2
        free(ret);      rc = idn2_to_ascii_lz(in, &ret, IDN2_NONTRANSITIONAL);
      if (rc == IDN2_DISALLOWED)
      if (nomem && (rc == IDNA_MALLOC_ERROR || rc == IDNA_DLOPEN_ERROR))        rc = idn2_to_ascii_lz(in, &ret, IDN2_TRANSITIONAL);
 #  else
       rc = idna_to_ascii_lz(in, &ret, 0);
 #  endif
       if (rc != IDNA_SUCCESS)
         {          {
          my_syslog(LOG_ERR, _("failed to allocate memory"));          if (ret)
          *nomem = 1;            free(ret);
           
           if (nomem && (rc == IDNA_MALLOC_ERROR || rc == IDNA_DLOPEN_ERROR))
             {
               my_syslog(LOG_ERR, _("failed to allocate memory"));
               *nomem = 1;
             }
           
           return NULL;
         }          }
          
      return NULL;      return ret;
     }      }
#else#endif
   
   if ((ret = whine_malloc(strlen(in)+1)))    if ((ret = whine_malloc(strlen(in)+1)))
     strcpy(ret, in);      strcpy(ret, in);
   else if (nomem)    else if (nomem)
     *nomem = 1;          *nomem = 1;    
 #endif  
   
   return ret;    return ret;
 }  }
   
unsigned char *do_rfc1035_name(unsigned char *p, char *sval)unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit)
 {  {
   int j;    int j;
       
   while (sval && *sval)    while (sval && *sval)
     {      {
       unsigned char *cp = p++;        unsigned char *cp = p++;
   
         if (limit && p > (unsigned char*)limit)
           return NULL;
   
       for (j = 0; *sval && (*sval != '.'); sval++, j++)        for (j = 0; *sval && (*sval != '.'); sval++, j++)
        *p++ = *sval;        {
           if (limit && p + 1 > (unsigned char*)limit)
             return NULL;
 
 #ifdef HAVE_DNSSEC
           if (option_bool(OPT_DNSSEC_VALID) && *sval == NAME_ESCAPE)
             *p++ = (*(++sval))-1;
           else
 #endif                
             *p++ = *sval;
         }
       
       *cp  = j;        *cp  = j;
       if (*sval)        if (*sval)
         sval++;          sval++;
     }      }
     
   return p;    return p;
 }  }
   
 /* for use during startup */  /* for use during startup */
 void *safe_malloc(size_t size)  void *safe_malloc(size_t size)
 {  {
  void *ret = malloc(size);  void *ret = calloc(1, size);
       
   if (!ret)    if (!ret)
     die(_("could not get memory"), NULL, EC_NOMEM);      die(_("could not get memory"), NULL, EC_NOMEM);
           
   return ret;    return ret;
}    }
   
   /* Ensure limited size string is always terminated.
    * Can be replaced by (void)strlcpy() on some platforms */
   void safe_strncpy(char *dest, const char *src, size_t size)
   {
     if (size != 0)
       {
         dest[size-1] = '\0';
         strncpy(dest, src, size-1);
       }
   }
   
 void safe_pipe(int *fd, int read_noblock)  void safe_pipe(int *fd, int read_noblock)
 {  {
   if (pipe(fd) == -1 ||     if (pipe(fd) == -1 || 
Line 239  void safe_pipe(int *fd, int read_noblock) Line 308  void safe_pipe(int *fd, int read_noblock)
   
 void *whine_malloc(size_t size)  void *whine_malloc(size_t size)
 {  {
  void *ret = malloc(size);  void *ret = calloc(1, size);
   
   if (!ret)    if (!ret)
     my_syslog(LOG_ERR, _("failed to allocate %d bytes"), (int) size);      my_syslog(LOG_ERR, _("failed to allocate %d bytes"), (int) size);
  
   return ret;    return ret;
 }  }
   
Line 255  int sockaddr_isequal(union mysockaddr *s1, union mysoc Line 324  int sockaddr_isequal(union mysockaddr *s1, union mysoc
           s1->in.sin_port == s2->in.sin_port &&            s1->in.sin_port == s2->in.sin_port &&
           s1->in.sin_addr.s_addr == s2->in.sin_addr.s_addr)            s1->in.sin_addr.s_addr == s2->in.sin_addr.s_addr)
         return 1;          return 1;
#ifdef HAVE_IPV6            
       if (s1->sa.sa_family == AF_INET6 &&        if (s1->sa.sa_family == AF_INET6 &&
           s1->in6.sin6_port == s2->in6.sin6_port &&            s1->in6.sin6_port == s2->in6.sin6_port &&
             s1->in6.sin6_scope_id == s2->in6.sin6_scope_id &&
           IN6_ARE_ADDR_EQUAL(&s1->in6.sin6_addr, &s2->in6.sin6_addr))            IN6_ARE_ADDR_EQUAL(&s1->in6.sin6_addr, &s2->in6.sin6_addr))
         return 1;          return 1;
 #endif  
     }      }
   return 0;    return 0;
 }  }
Line 270  int sa_len(union mysockaddr *addr) Line 339  int sa_len(union mysockaddr *addr)
 #ifdef HAVE_SOCKADDR_SA_LEN  #ifdef HAVE_SOCKADDR_SA_LEN
   return addr->sa.sa_len;    return addr->sa.sa_len;
 #else  #else
 #ifdef HAVE_IPV6  
   if (addr->sa.sa_family == AF_INET6)    if (addr->sa.sa_family == AF_INET6)
     return sizeof(addr->in6);      return sizeof(addr->in6);
   else    else
 #endif  
     return sizeof(addr->in);       return sizeof(addr->in); 
 #endif  #endif
 }  }
Line 299  int hostname_isequal(const char *a, const char *b) Line 366  int hostname_isequal(const char *a, const char *b)
       
   return 1;    return 1;
 }  }
    
 /* is b equal to or a subdomain of a return 2 for equal, 1 for subdomain */
 int hostname_issubdomain(char *a, char *b)
 {
   char *ap, *bp;
   unsigned int c1, c2;
   
   /* move to the end */
   for (ap = a; *ap; ap++); 
   for (bp = b; *bp; bp++);
 
   /* a shorter than b or a empty. */
   if ((bp - b) < (ap - a) || ap == a)
     return 0;
 
   do
     {
       c1 = (unsigned char) *(--ap);
       c2 = (unsigned char) *(--bp);
   
        if (c1 >= 'A' && c1 <= 'Z')
          c1 += 'a' - 'A';
        if (c2 >= 'A' && c2 <= 'Z')
          c2 += 'a' - 'A';
 
        if (c1 != c2)
          return 0;
     } while (ap != a);
 
   if (bp == b)
     return 2;
 
   if (*(--bp) == '.')
     return 1;
 
   return 0;
 }
  
   
 time_t dnsmasq_time(void)  time_t dnsmasq_time(void)
 {  {
 #ifdef HAVE_BROKEN_RTC  #ifdef HAVE_BROKEN_RTC
Line 315  time_t dnsmasq_time(void) Line 420  time_t dnsmasq_time(void)
 #endif  #endif
 }  }
   
   int netmask_length(struct in_addr mask)
   {
     int zero_count = 0;
   
     while (0x0 == (mask.s_addr & 0x1) && zero_count < 32) 
       {
         mask.s_addr >>= 1;
         zero_count++;
       }
     
     return 32 - zero_count;
   }
   
 int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)  int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
 {  {
   return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);    return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
 }   } 
   
 #ifdef HAVE_IPV6  
 int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen)  int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen)
 {  {
   int pfbytes = prefixlen >> 3;    int pfbytes = prefixlen >> 3;
Line 336  int is_same_net6(struct in6_addr *a, struct in6_addr * Line 453  int is_same_net6(struct in6_addr *a, struct in6_addr *
   return 0;    return 0;
 }  }
   
/* return least signigicant 64 bits if IPv6 address *//* return least significant 64 bits if IPv6 address */
 u64 addr6part(struct in6_addr *addr)  u64 addr6part(struct in6_addr *addr)
 {  {
   int i;    int i;
Line 359  void setaddr6part(struct in6_addr *addr, u64 host) Line 476  void setaddr6part(struct in6_addr *addr, u64 host)
     }      }
 }  }
   
 #endif  
    
   
 /* returns port number from address */  /* returns port number from address */
 int prettyprint_addr(union mysockaddr *addr, char *buf)  int prettyprint_addr(union mysockaddr *addr, char *buf)
 {  {
   int port = 0;    int port = 0;
       
 #ifdef HAVE_IPV6  
   if (addr->sa.sa_family == AF_INET)    if (addr->sa.sa_family == AF_INET)
     {      {
       inet_ntop(AF_INET, &addr->in.sin_addr, buf, ADDRSTRLEN);        inet_ntop(AF_INET, &addr->in.sin_addr, buf, ADDRSTRLEN);
Line 386  int prettyprint_addr(union mysockaddr *addr, char *buf Line 500  int prettyprint_addr(union mysockaddr *addr, char *buf
         }          }
       port = ntohs(addr->in6.sin6_port);        port = ntohs(addr->in6.sin6_port);
     }      }
 #else  
   strcpy(buf, inet_ntoa(addr->in.sin_addr));  
   port = ntohs(addr->in.sin_port);   
 #endif  
       
   return port;    return port;
 }  }
Line 402  void prettyprint_time(char *buf, unsigned int t) Line 512  void prettyprint_time(char *buf, unsigned int t)
     {      {
       unsigned int x, p = 0;        unsigned int x, p = 0;
        if ((x = t/86400))         if ((x = t/86400))
        p += sprintf(&buf[p], "%dd", x);        p += sprintf(&buf[p], "%ud", x);
        if ((x = (t/3600)%24))         if ((x = (t/3600)%24))
        p += sprintf(&buf[p], "%dh", x);        p += sprintf(&buf[p], "%uh", x);
       if ((x = (t/60)%60))        if ((x = (t/60)%60))
        p += sprintf(&buf[p], "%dm", x);        p += sprintf(&buf[p], "%um", x);
       if ((x = t%60))        if ((x = t%60))
        p += sprintf(&buf[p], "%ds", x);        p += sprintf(&buf[p], "%us", x);
     }      }
 }  }
   
Line 418  void prettyprint_time(char *buf, unsigned int t) Line 528  void prettyprint_time(char *buf, unsigned int t)
 int parse_hex(char *in, unsigned char *out, int maxlen,   int parse_hex(char *in, unsigned char *out, int maxlen, 
               unsigned int *wildcard_mask, int *mac_type)                unsigned int *wildcard_mask, int *mac_type)
 {  {
  int mask = 0, i = 0;  int done = 0, mask = 0, i = 0;
   char *r;    char *r;
           
   if (mac_type)    if (mac_type)
     *mac_type = 0;      *mac_type = 0;
       
  while (maxlen == -1 || i < maxlen)  while (!done && (maxlen == -1 || i < maxlen))
     {      {
       for (r = in; *r != 0 && *r != ':' && *r != '-' && *r != ' '; r++)        for (r = in; *r != 0 && *r != ':' && *r != '-' && *r != ' '; r++)
         if (*r != '*' && !isxdigit((unsigned char)*r))          if (*r != '*' && !isxdigit((unsigned char)*r))
           return -1;            return -1;
               
       if (*r == 0)        if (*r == 0)
        maxlen = i;        done = 1;
               
       if (r != in )        if (r != in )
         {          {
Line 454  int parse_hex(char *in, unsigned char *out, int maxlen Line 564  int parse_hex(char *in, unsigned char *out, int maxlen
                   int j, bytes = (1 + (r - in))/2;                    int j, bytes = (1 + (r - in))/2;
                   for (j = 0; j < bytes; j++)                    for (j = 0; j < bytes; j++)
                     {                       { 
                      char sav;                      char sav = sav;
                       if (j < bytes - 1)                        if (j < bytes - 1)
                         {                          {
                           sav = in[(j+1)*2];                            sav = in[(j+1)*2];
                           in[(j+1)*2] = 0;                            in[(j+1)*2] = 0;
                         }                          }
                         /* checks above allow mix of hexdigit and *, which
                            is illegal. */
                         if (strchr(&in[j*2], '*'))
                           return -1;
                       out[i] = strtol(&in[j*2], NULL, 16);                        out[i] = strtol(&in[j*2], NULL, 16);
                       mask = mask << 1;                        mask = mask << 1;
                      i++;                      if (++i == maxlen)
                         break; 
                       if (j < bytes - 1)                        if (j < bytes - 1)
                         in[(j+1)*2] = sav;                          in[(j+1)*2] = sav;
                     }                      }
Line 533  char *print_mac(char *buff, unsigned char *mac, int le Line 648  char *print_mac(char *buff, unsigned char *mac, int le
   return buff;    return buff;
 }  }
   
void bump_maxfd(int fd, int *max)/* rc is return from sendto and friends.
    Return 1 if we should retry.
    Set errno to zero if we succeeded. */
 int retry_send(ssize_t rc)
 {  {
  if (fd > *max)  static int retries = 0;
    *max = fd;  struct timespec waiter;
}  
   if (rc != -1)
     {
       retries = 0;
       errno = 0;
       return 0;
     }
   
   /* Linux kernels can return EAGAIN in perpetuity when calling
      sendmsg() and the relevant interface has gone. Here we loop
      retrying in EAGAIN for 1 second max, to avoid this hanging 
      dnsmasq. */
   
int retry_send(void)  if (errno == EAGAIN || errno == EWOULDBLOCK)
{ 
   struct timespec waiter; 
   if (errno == EAGAIN || errno == EWOULDBLOCK) 
      {       {
        waiter.tv_sec = 0;         waiter.tv_sec = 0;
        waiter.tv_nsec = 10000;         waiter.tv_nsec = 10000;
        nanosleep(&waiter, NULL);         nanosleep(&waiter, NULL);
       return 1;       if (retries++ < 1000)
          return 1;
      }       }
     
   if (errno == EINTR)  retries = 0;
     return 1;  
  if (errno == EINTR)
   return 0;    return 1;
   
   return 0;
 }  }
   
 int read_write(int fd, unsigned char *packet, int size, int rw)  int read_write(int fd, unsigned char *packet, int size, int rw)
Line 562  int read_write(int fd, unsigned char *packet, int size Line 691  int read_write(int fd, unsigned char *packet, int size
       
   for (done = 0; done < size; done += n)    for (done = 0; done < size; done += n)
     {      {
    retry:      do { 
      if (rw)        if (rw)
        n = read(fd, &packet[done], (size_t)(size - done));          n = read(fd, &packet[done], (size_t)(size - done));
      else        else
        n = write(fd, &packet[done], (size_t)(size - done));          n = write(fd, &packet[done], (size_t)(size - done));
         
         if (n == 0)
           return 0;
         
       } while (retry_send(n) || errno == ENOMEM || errno == ENOBUFS);
   
      if (n == 0)      if (errno != 0)
        return 0;        return 0;
      else if (n == -1) 
        { 
          if (retry_send() || errno == ENOMEM || errno == ENOBUFS) 
            goto retry; 
          else 
            return 0; 
        } 
     }      }
        
   return 1;    return 1;
 }  }
   
   /* close all fds except STDIN, STDOUT and STDERR, spare1, spare2 and spare3 */
   void close_fds(long max_fd, int spare1, int spare2, int spare3) 
   {
     /* On Linux, use the /proc/ filesystem to find which files
        are actually open, rather than iterate over the whole space,
        for efficiency reasons. If this fails we drop back to the dumb code. */
   #ifdef HAVE_LINUX_NETWORK 
     DIR *d;
     
     if ((d = opendir("/proc/self/fd")))
       {
         struct dirent *de;
   
         while ((de = readdir(d)))
           {
             long fd;
             char *e = NULL;
             
             errno = 0;
             fd = strtol(de->d_name, &e, 10);
                     
             if (errno != 0 || !e || *e || fd == dirfd(d) ||
                 fd == STDOUT_FILENO || fd == STDERR_FILENO || fd == STDIN_FILENO ||
                 fd == spare1 || fd == spare2 || fd == spare3)
               continue;
             
             close(fd);
           }
         
         closedir(d);
         return;
     }
   #endif
   
     /* fallback, dumb code. */
     for (max_fd--; max_fd >= 0; max_fd--)
       if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO && max_fd != STDIN_FILENO &&
           max_fd != spare1 && max_fd != spare2 && max_fd != spare3)
         close(max_fd);
   }
   
 /* Basically match a string value against a wildcard pattern.  */  /* Basically match a string value against a wildcard pattern.  */
 int wildcard_match(const char* wildcard, const char* match)  int wildcard_match(const char* wildcard, const char* match)
 {  {
Line 598  int wildcard_match(const char* wildcard, const char* m Line 767  int wildcard_match(const char* wildcard, const char* m
   
   return *wildcard == *match;    return *wildcard == *match;
 }  }
   
   /* The same but comparing a maximum of NUM characters, like strncmp.  */
   int wildcard_matchn(const char* wildcard, const char* match, int num)
   {
     while (*wildcard && *match && num)
       {
         if (*wildcard == '*')
           return 1;
   
         if (*wildcard != *match)
           return 0; 
   
         ++wildcard;
         ++match;
         --num;
       }
   
     return (!num) || (*wildcard == *match);
   }
   
   #ifdef HAVE_LINUX_NETWORK
   int kernel_version(void)
   {
     struct utsname utsname;
     int version;
     char *split;
     
     if (uname(&utsname) < 0)
       die(_("failed to find kernel version: %s"), NULL, EC_MISC);
     
     split = strtok(utsname.release, ".");
     version = (split ? atoi(split) : 0);
     split = strtok(NULL, ".");
     version = version * 256 + (split ? atoi(split) : 0);
     split = strtok(NULL, ".");
     return version * 256 + (split ? atoi(split) : 0);
   }
   #endif

Removed from v.1.1.1.1  
changed lines
  Added in v.1.1.1.4


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>