OSDN Git Service

* cygwin.din: Export gethostbyname2.
authorcorinna <corinna>
Fri, 6 Mar 2009 16:31:24 +0000 (16:31 +0000)
committercorinna <corinna>
Fri, 6 Mar 2009 16:31:24 +0000 (16:31 +0000)
* net.cc: define _CYGWIN_IN_H and include resolv.h.
(realloc_ent): New function.
(dup_ent): Call realloc_ent.
(memcpy4to6): New function.
(dn_length1): New function.
(gethostby_helper): New function.
(gethostbyname2): New function.
* posix.sgml: Add gethostbyname2.
* include/cygwin/version.h: Bump API minor number.
* libc/minires.c (get_options): Look for "inet6" and apply bounds
to "retry" and "retrans".
(res_ninit): Set the default options at the beginning.
(dn_expand): Fix "off by one".

winsup/cygwin/ChangeLog
winsup/cygwin/cygwin.din
winsup/cygwin/include/cygwin/version.h
winsup/cygwin/libc/minires.c
winsup/cygwin/net.cc
winsup/cygwin/posix.sgml

index c825055..c1adc07 100644 (file)
@@ -1,3 +1,20 @@
+2009-03-06  Pierre A. Humblet <pierre@phumblet.no-ip.org>
+
+       * cygwin.din: Export gethostbyname2.
+       * net.cc: define _CYGWIN_IN_H and include resolv.h.
+       (realloc_ent): New function.
+       (dup_ent): Call realloc_ent.
+       (memcpy4to6): New function.
+       (dn_length1): New function.
+       (gethostby_helper): New function.
+       (gethostbyname2): New function.
+       * posix.sgml: Add gethostbyname2.
+       * include/cygwin/version.h: Bump API minor number.
+       * libc/minires.c (get_options): Look for "inet6" and apply bounds
+       to "retry" and "retrans".
+       (res_ninit): Set the default options at the beginning.
+       (dn_expand): Fix "off by one".
+
 2009-03-06  Corinna Vinschen  <corinna@vinschen.de>
 
        * cygwin.din: Export wprintf, fwprintf, swprintf, vwprintf, vfwprintf,
index b55c9ce..6977b59 100644 (file)
@@ -636,6 +636,7 @@ _getgroups = getgroups SIGFE
 _getgroups32 = getgroups32 SIGFE
 gethostbyaddr = cygwin_gethostbyaddr SIGFE
 gethostbyname = cygwin_gethostbyname SIGFE
+gethostbyname2 SIGFE
 gethostid SIGFE
 gethostname = cygwin_gethostname SIGFE
 _gethostname = cygwin_gethostname SIGFE
index bbf9878..b51ddb6 100644 (file)
@@ -350,12 +350,13 @@ details. */
       199: Export open_wmemstream.
       200: Export mbsnrtowcs, wcsnrtombs.
       201: Export wprintf, fwprintf, swprintf, vwprintf, vfwprintf, vswprintf.
+      202: Export gethostbyname2.
      */
 
      /* Note that we forgot to bump the api for ualarm, strtoll, strtoull */
 
 #define CYGWIN_VERSION_API_MAJOR 0
-#define CYGWIN_VERSION_API_MINOR 201
+#define CYGWIN_VERSION_API_MINOR 202
 
      /* There is also a compatibity version number associated with the
        shared memory regions.  It is incremented when incompatible
index 8af24ac..606c05d 100644 (file)
@@ -1,6 +1,6 @@
 /* minires.c.  Stub synchronous resolver for Cygwin.
 
-   Copyright 2006, 2008 Red Hat, Inc.
+   Copyright 2006, 2008, 2009 Red Hat, Inc.
 
    Written by Pierre A. Humblet <Pierre.Humblet@ieee.org>
 
@@ -99,6 +99,11 @@ static void get_options(res_state statp, int i, char **words)
       DPRINTF(statp->options & RES_DEBUG, "%s: 1\n", words[i]);
       continue;
     }
+    if (!strcasecmp("inet6", words[i])) {
+      statp->options |= RES_USE_INET6;
+      DPRINTF(statp->options & RES_DEBUG, "%s: 1\n", words[i]);
+      continue;
+    }
     if (!strcasecmp("osquery", words[i])) {
       statp->use_os = 1;
       DPRINTF(statp->options & RES_DEBUG, "%s: 1\n", words[i]);
@@ -114,16 +119,22 @@ static void get_options(res_state statp, int i, char **words)
         continue;
         }
       */
-      if (!strcasecmp("retry", words[i])) {
+      if (!strcasecmp("retry", words[i])
+         || !strcasecmp("attempts", words[i])) {
        if (value < 1)
          value = 1;
+       else if (value > RES_MAXRETRY)
+         value = RES_MAXRETRY;
        statp->retry = value;
        DPRINTF(statp->options & RES_DEBUG, "%s: %d\n", words[i], value);
        continue;
       }
-      if (!strcasecmp("retrans", words[i])) {
+      if (!strcasecmp("retrans", words[i])
+         || !strcasecmp("timeout", words[i])) {
        if (value < 1)
          value = 1;
+       else if (value > RES_MAXRETRANS)
+         value = RES_MAXRETRANS;
        statp->retrans = value;
        DPRINTF(statp->options & RES_DEBUG, "%s: %d\n", words[i], value);
        continue;
@@ -270,6 +281,9 @@ int res_ninit(res_state statp)
   int i;
 
   statp->res_h_errno = NETDB_SUCCESS;
+   /* Only debug may be set before calling init */
+  statp->options &= RES_DEBUG;
+  statp->options |= RES_INIT | RES_DEFAULT;
   statp->nscount = 0;
   statp->os_query = NULL;
   statp->retrans = RES_TIMEOUT; /* timeout in seconds */
@@ -299,9 +313,6 @@ int res_ninit(res_state statp)
     statp->nsaddr_list[i].sin_port = htons(NAMESERVER_PORT);
     bzero(statp->nsaddr_list[i].sin_zero, sizeof(statp->nsaddr_list[i].sin_zero));
   }
-  /* Only debug may be set before calling init */
-  statp->options &= RES_DEBUG;
-  statp->options |= RES_INIT | RES_DEFAULT;
   return 0;
 }
 
@@ -806,7 +817,7 @@ int dn_expand(const unsigned char *msg, const unsigned char *eomorig,
     exp_dn++;
   else do {
     if (len <= MAXLABEL) {
-      if ((length -= (len + 1)) > 0 /* Need space for final . */
+      if ((length -= (len + 1)) >= 0 /* Need space for final . */
          && comp_dn + len <= eomorig) {
        do { *exp_dn++ = *comp_dn++; } while (--len != 0);
        *exp_dn++ = '.';
@@ -836,7 +847,6 @@ expand_fail:
   return -1;
 }
 
-
 /*****************************************************************
  *
  dn_comp
@@ -926,8 +936,7 @@ int dn_comp(const char * exp_dn, u_char * comp_dn, int length,
 }
 
 /*****************************************************************
- *
- dn_skipname
+ * dn_skipname
 
  Measures the compressed domain name length and returns it.
  *****************************************************************/
@@ -949,3 +958,38 @@ int dn_skipname(const unsigned char *comp_dn, const unsigned char *eom)
 
   return comp_dn - comp_dn_orig;
 }
+
+/*****************************************************************
+ * dn_length1    For internal use
+
+ Return length of uncompressesed name incl final 0.
+ *****************************************************************/
+
+int dn_length1(const unsigned char *msg, const unsigned char *eomorig,
+              const unsigned char *comp_dn)
+{
+  unsigned int len, length = 0;
+
+  errno = EINVAL;
+  if (comp_dn >= eomorig)
+    goto expand_fail;
+  else while ((len = *comp_dn++) != 0) {
+    if (len <= MAXLABEL) {
+      if ((comp_dn += len) <= eomorig)
+       length += len + 1;
+      else
+       goto expand_fail;
+    }
+    else if (len >= (128+64)) {
+      comp_dn = msg + (((len & ~(128+64)) << 8) + *comp_dn);
+      if (comp_dn >= eomorig)
+       goto expand_fail;
+    }
+    else
+      goto expand_fail;
+  }
+  return length;
+
+expand_fail:
+  return -1;
+}
index 8ffc041..132d639 100644 (file)
@@ -1,7 +1,7 @@
 /* net.cc: network-related routines.
 
    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-   2005, 2006, 2007 Red Hat, Inc.
+   2005, 2006, 2007, 2008, 2009 Red Hat, Inc.
 
 This file is part of Cygwin.
 
@@ -43,6 +43,8 @@ details. */
 #include "cygwin/in6.h"
 #include "ifaddrs.h"
 #include "tls_pbuf.h"
+#define _CYGWIN_IN_H 
+#include <resolv.h>
 
 extern "C"
 {
@@ -53,6 +55,9 @@ extern "C"
   int sscanf (const char *, const char *, ...);
   int cygwin_inet_aton(const char *, struct in_addr *);
   const char *cygwin_inet_ntop (int, const void *, char *, socklen_t);
+  int dn_length1(const unsigned char *, const unsigned char *,
+                const unsigned char *);
+  
 }                              /* End of "C" section */
 
 const struct in6_addr in6addr_any = {{IN6ADDR_ANY_INIT}};
@@ -264,6 +269,25 @@ struct pservent
 
 static const char *entnames[] = {"host", "proto", "serv"};
 
+static unionent *
+realloc_ent (unionent *&dst, int sz)
+{
+  /* Allocate the storage needed.  Allocate a rounded size to attempt to force
+     reuse of this buffer so that a poorly-written caller will not be using
+     a freed buffer. */
+  unsigned rsz = 256 * ((sz + 255) / 256);
+  unionent * ptr;
+  if ((ptr = (unionent *) realloc (dst, rsz)))
+    dst = ptr;
+  return ptr;
+}
+
+static inline hostent *
+realloc_ent (int sz, hostent *)
+{
+  return (hostent *) realloc_ent (_my_tls.locals.hostent_buf, sz);
+}
+
 /* Generic "dup a {host,proto,serv}ent structure" function.
    This is complicated because we need to be able to free the
    structure at any point and we can't rely on the pointer contents
@@ -310,7 +334,7 @@ dup_ent (unionent *&dst, unionent *src, unionent::struct_type type)
       break;
     }
 
-  /* Every *ent begins with a name.  Calculate it's length. */
+  /* Every *ent begins with a name.  Calculate its length. */
   int namelen = strlen_round (src->name);
   sz = struct_sz + namelen;
 
@@ -355,13 +379,8 @@ dup_ent (unionent *&dst, unionent *src, unionent::struct_type type)
        }
     }
 
-  /* Allocate the storage needed.  Allocate a rounded size to attempt to force
-     reuse of this buffer so that a poorly-written caller will not be using
-     a freed buffer. */
-  unsigned rsz = 256 * ((sz + 255) / 256);
-  dst = (unionent *) realloc (dst, rsz);
-
-  if (dst)
+  /* Allocate the storage needed.  */
+  if (realloc_ent (dst, sz))
     {
       memset (dst, 0, sz);
       /* This field is common to all *ent structures but named differently
@@ -857,6 +876,287 @@ cygwin_gethostbyaddr (const char *addr, int len, int type)
   return res;
 }
 
+static void 
+memcpy4to6 (char *dst, const u_char *src)
+{
+  const unsigned int h[] = {0, 0, htonl (0xFFFF)};
+  memcpy (dst, h, 12);
+  memcpy (dst + 12, src, NS_INADDRSZ);
+}
+
+static hostent * 
+gethostby_helper (const char *name, const int af, const int type,
+                 const int addrsize_in, const int addrsize_out)
+{
+  /* Get the data from the name server */
+  const int maxcount = 3;
+  int old_errno, ancount = 0, anlen = 1024, msgsize = 0;
+  u_char *ptr, *msg = NULL;
+  int sz;
+  hostent *ret;
+  char *string_ptr;
+
+  while ((anlen > msgsize) && (ancount++ < maxcount))
+    {
+      msgsize = anlen;
+      ptr = (u_char *) realloc (msg, msgsize);
+      if (ptr == NULL )
+       {
+         old_errno = errno;
+         free (msg);
+         set_errno (old_errno);
+         h_errno = NETDB_INTERNAL;
+         return NULL;
+       }
+      msg = ptr;
+      anlen = res_search (name, ns_c_in, type, msg, msgsize);
+    } 
+
+  if (ancount >= maxcount)
+    {
+      free (msg);
+      h_errno = NO_RECOVERY;
+      return NULL;
+    }
+  if (anlen < 0) /* errno and h_errno are set */
+    {
+      old_errno = errno;
+      free (msg);
+      set_errno (old_errno);
+      return NULL; 
+    }
+  u_char *eomsg = msg + anlen - 1;
+
+
+  /* We scan the answer records to determine the required memory size. 
+     They can be corrupted and we don't fully trust that the message
+     follows the standard exactly. glibc applies some checks that
+     we emulate.
+     The answers are copied in the hostent structure in a second scan.
+     To simplify the second scan we store information as follows:
+     - "class" is replaced by the compressed name size
+     - the first 16 bits of the "ttl" store the expanded name size + 1 
+     - the last 16 bits of the "ttl" store the offset to the next valid record.
+     Note that "type" is rewritten in host byte order. */
+  
+  class record {
+  public:
+    unsigned type: 16;         // type
+    unsigned complen: 16;       // class or compressed length
+    unsigned namelen1: 16;      // expanded length (with final 0)
+    unsigned next_o: 16;        // offset to next valid
+    unsigned size: 16;          // data size
+    u_char data[];              // data
+    record * next () { return (record *) (((char *) this) + next_o); }
+    void set_next ( record * nxt) { next_o = ((char *) nxt) - ((char *) this); }
+    u_char * name () { return (u_char *) (((char *) this) - complen); }
+  };
+
+  record * anptr = NULL, * prevptr = NULL, * curptr;
+  int i, alias_count = 0, string_size = 0, address_count = 0;
+  int complen, namelen1 = 0, address_len = 0, antype, anclass, ansize;
+
+  /* Get the count of answers */
+  ancount = ntohs (((HEADER *) msg)->ancount);
+
+  /* Skip the question, it was verified by res_send */
+  ptr = msg + sizeof (HEADER);
+  if ((complen = dn_skipname (ptr, eomsg)) < 0)
+    goto corrupted;    
+  /* Point to the beginning of the answer section */
+  ptr += complen + NS_QFIXEDSZ;
+  
+  /* Scan the answer records to determine the sizes */
+  for (i = 0; i < ancount; i++, ptr = curptr->data + ansize)
+    {
+      if ((complen = dn_skipname (ptr, eomsg)) < 0)
+       goto corrupted;
+
+      curptr = (record *) (ptr + complen);
+      antype = ntohs (curptr->type);
+      anclass = ntohs (curptr->complen);
+      ansize = ntohs (curptr->size);
+      /* Class must be internet */
+      if (anclass != ns_c_in)
+       continue;
+
+      curptr->complen = complen;
+      if ((namelen1 = dn_length1 (msg, eomsg, curptr-> name())) <= 0)
+       goto corrupted;
+
+      if (antype == ns_t_cname) 
+       {
+         alias_count++;
+         string_size += namelen1;
+       }
+      else if (antype == type)
+       {
+         ansize = ntohs (curptr->size);
+         if (ansize != addrsize_in)
+           continue;
+         if (address_count == 0)
+           {
+             address_len = namelen1;
+             string_size += namelen1;
+           }
+         else if (address_len != namelen1)
+           continue;
+         address_count++;
+       }
+      /* Update the records */
+      curptr->type = antype; /* Host byte order */
+      curptr->namelen1 = namelen1;
+      if (! anptr)
+       anptr = prevptr = curptr;
+      else
+       {
+         prevptr->set_next (curptr);
+         prevptr = curptr;
+       }
+    }
+
+  /* If there is no address, quit */
+  if (address_count == 0)
+    {
+      free (msg);
+      h_errno = NO_DATA;
+      return NULL;
+    }
+  
+  /* Determine the total size */
+  sz = DWORD_round (sizeof(hostent))
+       + sizeof (char *) * (alias_count + address_count + 2)
+       + string_size
+       + address_count * addrsize_out;
+
+  ret = realloc_ent (sz,  (hostent *) NULL);
+  if (! ret) 
+    {
+      old_errno = errno;
+      free (msg);
+      set_errno (old_errno);
+      h_errno = NETDB_INTERNAL;
+      return NULL;
+    }
+
+  ret->h_addrtype = af;
+  ret->h_length = addrsize_out;
+  ret->h_aliases = (char **) (((char *) ret) + DWORD_round (sizeof(hostent)));
+  ret->h_addr_list = ret->h_aliases + alias_count + 1;
+  string_ptr = (char *) (ret->h_addr_list + address_count + 1);
+  /* Rescan the answers */
+  ancount = alias_count + address_count; /* Valid records */
+  alias_count = address_count = 0;
+
+  for (i = 0, curptr = anptr; i < ancount; i++, curptr = curptr->next ())
+    {
+      antype = curptr->type;
+      if (antype == ns_t_cname) 
+       {
+         complen = dn_expand (msg, eomsg, curptr->name (), string_ptr, string_size);
+#ifdef DEBUGGING
+         if (complen != curptr->complen)  
+           go to debugging;
+#endif
+         ret->h_aliases[alias_count++] = string_ptr;
+         namelen1 = curptr->namelen1;
+         string_ptr += namelen1;
+         string_size -= namelen1;        
+         continue;
+       }
+      if (antype == type)
+           {
+             if (address_count == 0)
+               {
+                 complen = dn_expand (msg, eomsg, curptr->name(), string_ptr, string_size);
+#ifdef DEBUGGING
+                 if (complen != curptr->complen)  
+                   go to debugging;
+#endif
+                 ret->h_name = string_ptr;
+                 namelen1 = curptr->namelen1;
+                 string_ptr += namelen1;
+                 string_size -= namelen1;        
+               }
+             ret->h_addr_list[address_count++] = string_ptr;
+             if (addrsize_in != addrsize_out)
+               memcpy4to6 (string_ptr, curptr->data);
+             else
+               memcpy (string_ptr, curptr->data, addrsize_in);
+             string_ptr += addrsize_out;
+             string_size -= addrsize_out;
+             continue;
+           }
+#ifdef DEBUGGING
+      /* Should not get here */
+      go to debugging;
+#endif
+    }
+#ifdef DEBUGGING
+  if (string_size < 0)  
+    go to debugging;
+#endif      
+  
+  free (msg);
+
+  ret->h_aliases[alias_count] = NULL;
+  ret->h_addr_list[address_count] = NULL;
+  return ret;
+
+ corrupted:
+  free (msg);
+  /* Hopefully message corruption errors are temporary.
+     Should it be NO_RECOVERY ? */
+  h_errno = TRY_AGAIN;
+  return NULL;
+
+
+#ifdef DEBUGGING
+ debugging:
+   system_printf ("Please debug.");
+   free (msg);
+   free (ret);
+   h_errno = NO_RECOVERY;
+   return NULL;
+#endif
+}
+
+/* gethostbyname2: standards? */
+extern "C" struct hostent *
+gethostbyname2 (const char *name, int af)
+{
+  sig_dispatch_pending ();
+  myfault efault;
+  if (efault.faulted (EFAULT))
+    return NULL;
+
+  if (!(_res.options & RES_INIT))
+      res_init();
+  bool v4to6 = _res.options & RES_USE_INET6;
+
+  int type, addrsize_in, addrsize_out;
+  switch (af) 
+    {
+    case AF_INET:
+      addrsize_in = NS_INADDRSZ;
+      addrsize_out = (v4to6) ? NS_IN6ADDRSZ : NS_INADDRSZ;
+      type = ns_t_a;
+      break;
+    case AF_INET6:
+      addrsize_in = addrsize_out = NS_IN6ADDRSZ;
+      type = ns_t_aaaa;
+      break;
+    default:
+      set_errno (EAFNOSUPPORT);
+      h_errno = NETDB_INTERNAL;
+      return NULL;
+    }
+  
+  return gethostby_helper (name, af, type, addrsize_in, addrsize_out);
+}
+
 /* exported as accept: standards? */
 extern "C" int
 cygwin_accept (int fd, struct sockaddr *peer, socklen_t *len)
@@ -2486,10 +2786,6 @@ cygwin_sendmsg (int fd, const struct msghdr *msg, int flags)
  * SOFTWARE.
  */
 
-#define        IN6ADDRSZ       16
-#define        INADDRSZ         4
-#define        INT16SZ          2
-
 /* int
  * inet_pton4(src, dst)
  *     like inet_aton() but without all the hexadecimal and shorthand.
index c97d09d..ab33515 100644 (file)
@@ -1067,6 +1067,7 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008).</para>
     gcvt                       (SUSv3)
     gethostbyaddr              (SUSv3)
     gethostbyname              (SUSv3)
+    gethostbyname2             (first defined in BIND 4.9.4)
     getpass                    (SUSv2)
     getutent                   (XPG2)
     getutid                    (XPG2)