[Patch] gethostbyname2

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

[Patch] gethostbyname2

Pierre A. Humblet-2
I tried to compile Exim with IPv6 enabled and Cygwin 1.7, but it
needs gethostbyname2.
Here is an implementation of that function.
In attachment I am including the same patch as well as a short test function.

Pierre



2009-02-25  Pierre Humblet <[hidden email]>

        * net.cc: Include windns.h.
        (gethostbyname2): New function.
        * cygwin.din: Export gethostbyname2.


Index: cygwin.din
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/cygwin.din,v
retrieving revision 1.202
diff -u -p -r1.202 cygwin.din
--- cygwin.din 19 Feb 2009 09:22:51 -0000 1.202
+++ cygwin.din 26 Feb 2009 03:54:30 -0000
@@ -635,6 +635,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: net.cc
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/net.cc,v
retrieving revision 1.248
diff -u -p -r1.248 net.cc
--- net.cc 16 Sep 2008 02:04:27 -0000 1.248
+++ net.cc 26 Feb 2009 03:54:31 -0000
@@ -25,6 +25,7 @@ details. */
  #include <netdb.h>
  #define USE_SYS_TYPES_FD_SET
  #include <winsock2.h>
+#include <windns.h>
  #include <iphlpapi.h>
  #include <assert.h>
  #include "cygerrno.h"
@@ -888,6 +889,157 @@ cygwin_gethostbyaddr (const char *addr,
    return res;
  }

+/* gethostbyname2: standards? */
+extern "C" struct hostent *
+gethostbyname2 (const char *name, int af)
+{
+  sig_dispatch_pending ();
+  myfault efault;
+  if (efault.faulted (EFAULT))
+    return NULL;
+
+  DWORD type;
+  hostent tmp, *h = NULL;
+  tmp.h_name = NULL;
+  tmp.h_aliases = NULL;
+  tmp.h_addrtype = af;
+  switch (af)
+    {
+    case AF_INET:
+      tmp.h_length = sizeof (IP4_ADDRESS);
+      type = DNS_TYPE_A;
+      break;
+    case AF_INET6:
+      tmp.h_length = sizeof (IP6_ADDRESS);
+      type = DNS_TYPE_AAAA;
+      break;
+    default:
+      set_errno (EAFNOSUPPORT);
+      h_errno = NETDB_INTERNAL;
+      return NULL;
+    }
+
+  PDNS_RECORD pQueryResultsSet, rr;
+  /* Normal query, including search */
+  DNS_STATUS res = DnsQuery_A (name, type, DNS_QUERY_STANDARD,
+       NULL, &pQueryResultsSet, NULL);
+
+  debug_printf ("DnsQuery: %s type = %d res = %lu\n", name, type, res);
+
+  // NETDB_INTERNAL -1 /* see errno */
+  // HOST_NOT_FOUND  1 /* Authoritative Answer Host not found */
+  // TRY_AGAIN       2 /* Non-Authoritive Host not found, or SERVERFAIL */
+  // NO_RECOVERY     3 /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */
+  // NO_DATA         4 /* Valid name, no data record of requested type */
+
+  switch (res)
+    {
+    case ERROR_SUCCESS:
+      break;
+    case ERROR_INVALID_NAME:
+      set_errno (EINVAL);
+      h_errno = NETDB_INTERNAL;;
+      return NULL;
+    case ERROR_TIMEOUT:
+    case DNS_ERROR_RCODE_SERVER_FAILURE:
+      h_errno = TRY_AGAIN;
+      return NULL;
+    case DNS_ERROR_RCODE_NAME_ERROR:
+      h_errno = HOST_NOT_FOUND;
+      return NULL;
+    case DNS_ERROR_NO_DNS_SERVERS:
+    case DNS_ERROR_RCODE_FORMAT_ERROR:
+    case DNS_ERROR_RCODE_NOT_IMPLEMENTED:
+    case DNS_ERROR_RCODE_REFUSED:
+      h_errno = NO_RECOVERY;
+      return NULL;
+    case DNS_INFO_NO_RECORDS:  /* May be returned when the host
doesn't exist */
+      h_errno = NO_DATA;
+      return NULL;
+    default:
+      debug_printf ("Unknown code\n");
+      h_errno = NO_RECOVERY;
+      return NULL;
+    }
+
+  const int RECORD_INC = 16;
+  int record_size = 0;
+  int alias_count = 0;
+  int address_count = 0;
+  for (rr = pQueryResultsSet; rr; rr = rr->pNext)
+    {
+      debug_printf ("%s Section %d Type %u Size %d\n",
+    rr->pName, rr->Flags.S.Section, rr->wType, rr->wDataLength);
+      /* Normally DnsQuery does not provide Question section records and we
+ should only consider Answer section records.
+ However DnsQuery seems to return only Question records (with data)
+ for local names and numeric addresses */
+      if (!((rr->Flags.S.Section == DnsSectionAnswer)
+    || (rr->Flags.S.Section == DnsSectionQuestion)))
+ continue;
+
+      /* Alloc space, including two null entries */
+      if ((alias_count + address_count + 2) >= record_size)
+ {
+  void * ptr = realloc (tmp.h_aliases,
+ sizeof (char *)*(record_size += RECORD_INC));
+  if (!ptr)
+    {
+      h_errno = NETDB_INTERNAL;
+      address_count = -1; /* Flag error */
+      break;
+    }
+  tmp.h_aliases = (char **) ptr;
+ }
+
+      switch (rr->wType)
+ {
+ case DNS_TYPE_CNAME:
+  /* The CNAME records are supposed to form a chain.
+     We trust the resolver.
+     Move the addresses. This should never be needed. */
+  for (int i = address_count; i > 0; i--)
+    tmp.h_aliases[alias_count + 1 + i] = tmp.h_aliases[alias_count + i];
+  tmp.h_aliases[alias_count++] = rr->pName;
+  break;
+ case DNS_TYPE_A:
+  /* The address records should all have as pName the last canonical name.
+     We trust the resolver */
+  if ((af == AF_INET)
+      && (rr->wDataLength == sizeof (IP4_ADDRESS)))
+    {
+      tmp.h_aliases[alias_count + ++address_count] = (char *) &
rr->Data.A.IpAddress;
+      tmp.h_name = rr->pName;
+    }
+  break;
+ case DNS_TYPE_AAAA:
+  if ((af == AF_INET6)
+      && (rr->wDataLength == sizeof (IP6_ADDRESS)))
+    {
+      tmp.h_aliases[alias_count + ++address_count] = (char *) &
rr->Data.AAAA.Ip6Address;
+      tmp.h_name = rr->pName;
+    }
+  break;
+ default:
+  continue;
+ }
+      }
+
+  if (address_count == 0)
+    h_errno = NO_DATA;
+  else if (address_count > 0)
+    {
+      tmp.h_aliases[alias_count] = NULL;
+      tmp.h_addr_list = & tmp.h_aliases[alias_count + 1];
+      tmp.h_addr_list[address_count] = NULL;
+      if (!(h = dup_ent (&tmp)))
+ h_errno = NETDB_INTERNAL;
+    }
+  DnsRecordListFree (pQueryResultsSet, DnsFreeRecordList);
+  free (tmp.h_aliases);
+  return h;
+}
+
  /* exported as accept: standards? */
  extern "C" int
  cygwin_accept (int fd, struct sockaddr *peer, socklen_t *len)

try_gethostbyname.c (1K) Download Attachment
net.cc (152K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [Patch] gethostbyname2

Corinna Vinschen-2
On Feb 25 23:03, Pierre A. Humblet wrote:

> I tried to compile Exim with IPv6 enabled and Cygwin 1.7, but it needs
> gethostbyname2.
> Here is an implementation of that function.
> In attachment I am including the same patch as well as a short test function.
>
> Pierre
>
>
>
> 2009-02-25  Pierre Humblet <[hidden email]>
>
> * net.cc: Include windns.h.
> (gethostbyname2): New function.
> * cygwin.din: Export gethostbyname2.

This is way cool!  I have this function on my TODO list for ages.

But there's a problem.  You're using DnsQuery_A directly, but this
function only exists since Win2K.  Would it be a big problem to rework
the function to use the resolver functions instead?  They are part of
Cygwin now anyway and that would abstract gethostbyname2 from the
underlying OS capabilities.

The implications of having this function... for instance, we can
implement gethostbyname then in terms of gethostbyname2 and honor
the RES_USE_INET6 flag.  We could even drop relying on the Winsock
implementation of getaddrinfo/getnameinfo and use the Stevens
implementation exclusively.  Wow.


Thanks,
Corinna

--
Corinna Vinschen                  Please, send mails regarding Cygwin to
Cygwin Project Co-Leader          cygwin AT cygwin DOT com
Red Hat
Reply | Threaded
Open this post in threaded view
|

Re: [Patch] gethostbyname2

Pierre A. Humblet-2
In reply to this post by Pierre A. Humblet-2
At 04:52 AM 2/26/2009, Corinna Vinschen wrote:
| >On Feb 25 23:03, Pierre A. Humblet wrote:
| > > I tried to compile Exim with IPv6 enabled and Cygwin 1.7, but it needs
| > > gethostbyname2.
| > > Here is an implementation of that function.
| > > In attachment I am including the same patch as well as a short test function.
| > >
| >
| >This is way cool!  I have this function on my TODO list for ages.
| >
| >But there's a problem.  You're using DnsQuery_A directly, but this
| >function only exists since Win2K.  Would it be a big problem to rework
| >the function to use the resolver functions instead?  They are part of
| >Cygwin now anyway and that would abstract gethostbyname2 from the
| >underlying OS capabilities.

I was afraid of that. Using res_query was my initial thought, but I realized that when using the
Windows resolver I would undo in gethostbyname2 all the work done in minires.

I am wondering if gethostbyname2 should not be moved out of  net.cc and integrated
with minires. We could design shortcuts to use the most appropriate method.

I have read RFC 2133, section 6.1 . Do we want to implement having a
RES_OPTIONS in the environment,  in  /etc/resolv.conf, or only by setting the
appropriate flag in _res? What does Linux do?

I am still fighting one issue with Windows. On XP, when using the native gethostbyname
I can resolve computers on my local net (through NetBIOS or such). But I can't get
them with DnsQuery, except my own computer, despite what I think the doc says.
Any insight?

Pierre

Reply | Threaded
Open this post in threaded view
|

Re: [Patch] gethostbyname2

Corinna Vinschen-2
On Feb 26 10:29, Pierre A. Humblet wrote:

> At 04:52 AM 2/26/2009, Corinna Vinschen wrote:
> | >On Feb 25 23:03, Pierre A. Humblet wrote:
> | > > I tried to compile Exim with IPv6 enabled and Cygwin 1.7, but it needs
> | > > gethostbyname2.
> | > > Here is an implementation of that function.
> | > > In attachment I am including the same patch as well as a short test function.
> | > >
> | >
> | >This is way cool!  I have this function on my TODO list for ages.
> | >
> | >But there's a problem.  You're using DnsQuery_A directly, but this
> | >function only exists since Win2K.  Would it be a big problem to rework
> | >the function to use the resolver functions instead?  They are part of
> | >Cygwin now anyway and that would abstract gethostbyname2 from the
> | >underlying OS capabilities.
>
> I was afraid of that. Using res_query was my initial thought, but I realized that when using the
> Windows resolver I would undo in gethostbyname2 all the work done in minires.

I'm sorry, but I really don't understand what you mean.  How are you
undoing work in minires when using minires in gethostbyname2?!?  Why
isn't it just possible to call res_query from there?

> I am wondering if gethostbyname2 should not be moved out of  net.cc and integrated
> with minires. We could design shortcuts to use the most appropriate method.

I must be missing something serious here.  I'm puzzled why it matters
where gethostbyname2 is.  I was thinking of the resolver being basic
functionality.  The idea was to implement practically all subsequent
functions like gethostbyname, gethostbyaddr, getaddrinfo in terms of
resolver functions, as it is done in other libraries as well at one
point.  Why should applications be able to call res_query but not
Cygwin itself?

> I have read RFC 2133, section 6.1 . Do we want to implement having a
> RES_OPTIONS in the environment,  in  /etc/resolv.conf, or only by setting the
> appropriate flag in _res? What does Linux do?

Both.  But, if the general functionality (flag in _res) is present, we
can always add more functionality at some later point.  It's not a
pressing issue, I guess.

> I am still fighting one issue with Windows. On XP, when using the native gethostbyname
> I can resolve computers on my local net (through NetBIOS or such). But I can't get
> them with DnsQuery, except my own computer, despite what I think the doc says.
> Any insight?

I never used the DnsQuery functions myself.  There's a DnsQuery flag
called DNS_QUERY_NO_NETBT documented in MSDN, maybe there's something
switched off on your machine so that's the default?


Corinna

--
Corinna Vinschen                  Please, send mails regarding Cygwin to
Cygwin Project Co-Leader          cygwin AT cygwin DOT com
Red Hat
Reply | Threaded
Open this post in threaded view
|

Re: [Patch] gethostbyname2

Dave Korn-6
In reply to this post by Pierre A. Humblet-2
Pierre A. Humblet wrote:

> I am still fighting one issue with Windows. On XP, when using the native
> gethostbyname I can resolve computers on my local net (through NetBIOS or
> such). But I can't get them with DnsQuery, except my own computer, despite
> what I think the doc says. Any insight?

  Are you running / not-running the "DNS Client" service?  What happens if you
do the opposite of however it is now?  (Or even just restart it, if already
running, sometimes it gets persistently confused).

    cheers,
      DaveK
Reply | Threaded
Open this post in threaded view
|

Re: [Patch] gethostbyname2

Pierre A. Humblet-2
In reply to this post by Corinna Vinschen-2

----- Original Message -----
From: "Corinna Vinschen"
To: <cygwin-patches>
Sent: Thursday, February 26, 2009 11:05 AM
Subject: Re: [Patch] gethostbyname2


| On Feb 26 10:29, Pierre A. Humblet wrote:
| > At 04:52 AM 2/26/2009, Corinna Vinschen wrote:
| > | >On Feb 25 23:03, Pierre A. Humblet wrote:
| > | > > I tried to compile Exim with IPv6 enabled and Cygwin 1.7, but it needs
| > | > > gethostbyname2.
| > | > > Here is an implementation of that function.
| > | > > In attachment I am including the same patch as well as a short test function.
| > | > >
| > | >
| > | >This is way cool!  I have this function on my TODO list for ages.
| > | >
| > | >But there's a problem.  You're using DnsQuery_A directly, but this
| > | >function only exists since Win2K.  Would it be a big problem to rework
| > | >the function to use the resolver functions instead?  They are part of
| > | >Cygwin now anyway and that would abstract gethostbyname2 from the
| > | >underlying OS capabilities.
| >
| > I was afraid of that. Using res_query was my initial thought, but I realized that when using
the
| > Windows resolver I would undo in gethostbyname2 all the work done in minires.
|
| I'm sorry, but I really don't understand what you mean.  How are you
| undoing work in minires when using minires in gethostbyname2?!?  Why
| isn't it just possible to call res_query from there?

It is possible of course. But the sequence would be the following
1) External DNS server sends compressed records to Windows resolver
2) Windows resolver uncompresses the records and puts them in nice structures
3) Minires takes the nice structures and recompresses them to wire format
4) Gethostbyname2 uncompresses them into nice structures
    then 5) calls dup_ent,  which copies them in the tls.locals
I would like to streamline the process:
- With Windows resolver: 1, 2, 5  (that's the current patch, ~20% of  which is
         cut&pasted from minires and could be restructured to use a common function)
- Without: Straight fom wire format records to the tls.locals memory block
                 Or: have a routine 2a) in minires that replaces 2. Then it would be 1, 2a, 5.

| > I am still fighting one issue with Windows. On XP, when using the native gethostbyname
| > I can resolve computers on my local net (through NetBIOS or such). But I can't get
| > them with DnsQuery, except my own computer, despite what I think the doc says.
| > Any insight?
|
| I never used the DnsQuery functions myself.  There's a DnsQuery flag
| called DNS_QUERY_NO_NETBT documented in MSDN, maybe there's something
| switched off on your machine so that's the default?

Perhaps. But where is it or how does the native gethostbyname turn it on?
To answer Dave, "DNS Client" service is running, I have never experimented turning it off.

Pierre

Reply | Threaded
Open this post in threaded view
|

Re: [Patch] gethostbyname2

Corinna Vinschen-2
On Feb 26 12:55, Pierre A. Humblet wrote:

> From: "Corinna Vinschen"
> | I'm sorry, but I really don't understand what you mean.  How are you
> | undoing work in minires when using minires in gethostbyname2?!?  Why
> | isn't it just possible to call res_query from there?
>
> It is possible of course. But the sequence would be the following
> 1) External DNS server sends compressed records to Windows resolver
> 2) Windows resolver uncompresses the records and puts them in nice structures
> 3) Minires takes the nice structures and recompresses them to wire format
> 4) Gethostbyname2 uncompresses them into nice structures
>     then 5) calls dup_ent,  which copies them in the tls.locals
> I would like to streamline the process:
> - With Windows resolver: 1, 2, 5  (that's the current patch, ~20% of  which is
>          cut&pasted from minires and could be restructured to use a common function)
> - Without: Straight fom wire format records to the tls.locals memory block
>                  Or: have a routine 2a) in minires that replaces 2. Then it would be 1, 2a, 5.

Uh, I see.  Well, you could change the minires code so that there is an
internal interface which can be used from net.cc or, FWIW, any other
part of Cygwin to optimize the above process.  If you really think
it's necessary to move gethostbyname2 to the minires sources, go for it.
However, in the long run I think it's better to keep gethostbyname2 in
net.cc and to have an optimized internal resolver interface for this
kind of call.

> | I never used the DnsQuery functions myself.  There's a DnsQuery flag
> | called DNS_QUERY_NO_NETBT documented in MSDN, maybe there's something
> | switched off on your machine so that's the default?
>
> Perhaps. But where is it or how does the native gethostbyname turn it on?

Did you search on the net?  I have honestly no idea why this happens and
I would have to do the same, sorry.


Corinna

--
Corinna Vinschen                  Please, send mails regarding Cygwin to
Cygwin Project Co-Leader          cygwin AT cygwin DOT com
Red Hat