static PyObject *
Channel_servers_get(Channel *self, void *closure)
{
    int r;
    char ip[INET6_ADDRSTRLEN];
    struct ares_addr_node *server, *servers;
    PyObject *server_list;
    PyObject *tmp;

    UNUSED_ARG(closure);

    if (!self->channel) {
        PyErr_SetString(PyExc_AresError, "Channel has already been destroyed");
        return NULL;
    }

    server_list = PyList_New(0);
    if (!server_list) {
        PyErr_NoMemory();
        return NULL;
    }

    r = ares_get_servers(self->channel, &servers);
    if (r != ARES_SUCCESS) {
        RAISE_ARES_EXCEPTION(r);
        return NULL;
    }

    for (server = servers; server != NULL; server = server->next) {
        if (server->family == AF_INET) {
            ares_inet_ntop(AF_INET, &(server->addr.addr4), ip, INET_ADDRSTRLEN);
            tmp = Py_BuildValue("s", ip);
        } else {
            ares_inet_ntop(AF_INET6, &(server->addr.addr6), ip, INET6_ADDRSTRLEN);
            tmp = Py_BuildValue("s", ip);
        }
        if (tmp == NULL) {
            break;
        }
        r = PyList_Append(server_list, tmp);
        Py_DECREF(tmp);
        if (r != 0) {
            break;
        }
    }

    return server_list;
}
Exemplo n.º 2
0
static void callback(void *arg, int status, int timeouts, struct hostent *host)
{
  char **p;

  (void)timeouts;

  if (status != ARES_SUCCESS)
    {
      fprintf(stderr, "%s: %s\n", (char *) arg, ares_strerror(status));
      return;
    }

  for (p = host->h_addr_list; *p; p++)
    {
      char addr_buf[46] = "??";

      ares_inet_ntop(host->h_addrtype, *p, addr_buf, sizeof(addr_buf));
      printf("%-32s\t%s", host->h_name, addr_buf);
#if 0
      if (host->h_aliases[0])
        {
           int i;

           printf (", Aliases: ");
           for (i = 0; host->h_aliases[i]; i++)
               printf("%s ", host->h_aliases[i]);
        }
#endif
      puts("");
    }
}
Exemplo n.º 3
0
static int
gevent_append_addr(PyObject* list, int family, void* src, char* tmpbuf, size_t tmpsize) {
    int status = -1;
    PyObject* tmp;
    if (ares_inet_ntop(family, src, tmpbuf, tmpsize)) {
        tmp = PyBytes_FromString(tmpbuf);
        if (tmp) {
            status = PyList_Append(list, tmp);
            Py_DECREF(tmp);
        }
    }
    return status;
}
Exemplo n.º 4
0
void ares_getnameinfo(ares_channel channel, const struct sockaddr *sa,
                      ares_socklen_t salen,
                      int flags, ares_nameinfo_callback callback, void *arg)
{
  struct sockaddr_in *addr = NULL;
  struct sockaddr_in6 *addr6 = NULL;
  struct nameinfo_query *niquery;
  unsigned int port = 0;

  /* Verify the buffer size */
  if (salen == sizeof(struct sockaddr_in))
    {
      addr = (struct sockaddr_in *)sa;
      port = addr->sin_port;
    }
  else if (salen == sizeof(struct sockaddr_in6))
    {
      addr6 = (struct sockaddr_in6 *)sa;
      port = addr6->sin6_port;
    }
  else
    {
      callback(arg, ARES_ENOTIMP, 0, NULL, NULL);
      return;
    }

  /* If neither, assume they want a host */
  if (!(flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
    flags |= ARES_NI_LOOKUPHOST;

  /* All they want is a service, no need for DNS */
  if ((flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
    {
      char buf[33], *service;

      service = lookup_service((unsigned short)(port & 0xffff),
                               flags, buf, sizeof(buf));
      callback(arg, ARES_SUCCESS, 0, NULL, service);
      return;
    }

  /* They want a host lookup */
  if ((flags & ARES_NI_LOOKUPHOST))
    {
     /* A numeric host can be handled without DNS */
     if ((flags & ARES_NI_NUMERICHOST))
      {
        char ipbuf[IPBUFSIZ];
        char srvbuf[33];
        char *service = NULL;
        ipbuf[0] = 0;

        /* Specifying not to lookup a host, but then saying a host
         * is required has to be illegal.
         */
        if (flags & ARES_NI_NAMEREQD)
          {
            callback(arg, ARES_EBADFLAGS, 0, NULL, NULL);
            return;
          }
        if (salen == sizeof(struct sockaddr_in6))
          {
            ares_inet_ntop(AF_INET6, &addr6->sin6_addr, ipbuf, IPBUFSIZ);
            /* If the system supports scope IDs, use it */
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
            append_scopeid(addr6, flags, ipbuf, sizeof(ipbuf));
#endif
          }
        else
          {
            ares_inet_ntop(AF_INET, &addr->sin_addr, ipbuf, IPBUFSIZ);
          }
        /* They also want a service */
        if (flags & ARES_NI_LOOKUPSERVICE)
          service = lookup_service((unsigned short)(port & 0xffff),
                                   flags, srvbuf, sizeof(srvbuf));
        callback(arg, ARES_SUCCESS, 0, ipbuf, service);
        return;
      }
    /* This is where a DNS lookup becomes necessary */
    else
      {
        niquery = malloc(sizeof(struct nameinfo_query));
        if (!niquery)
          {
            callback(arg, ARES_ENOMEM, 0, NULL, NULL);
            return;
          }
        niquery->callback = callback;
        niquery->arg = arg;
        niquery->flags = flags;
        niquery->timeouts = 0;
        if (sa->sa_family == AF_INET)
          {
            niquery->family = AF_INET;
            memcpy(&niquery->addr.addr4, addr, sizeof(addr));
            ares_gethostbyaddr(channel, &addr->sin_addr, sizeof(struct in_addr), AF_INET,
                               nameinfo_callback, niquery);
          }
        else
          {
            niquery->family = AF_INET6;
            memcpy(&niquery->addr.addr6, addr6, sizeof(addr6));
            ares_gethostbyaddr(channel, &addr6->sin6_addr, sizeof(struct in6_addr), AF_INET6,
                               nameinfo_callback, niquery);
          }
      }
    }
}
Exemplo n.º 5
0
static void nameinfo_callback(void *arg, int status, int timeouts, struct hostent *host)
{
  struct nameinfo_query *niquery = (struct nameinfo_query *) arg;
  char srvbuf[33];
  char *service = NULL;

  niquery->timeouts += timeouts;
  if (status == ARES_SUCCESS)
    {
      /* They want a service too */
      if (niquery->flags & ARES_NI_LOOKUPSERVICE)
        {
          if (niquery->family == AF_INET)
            service = lookup_service(niquery->addr.addr4.sin_port,
                                     niquery->flags, srvbuf, sizeof(srvbuf));
          else
            service = lookup_service(niquery->addr.addr6.sin6_port,
                                     niquery->flags, srvbuf, sizeof(srvbuf));
        }
      /* NOFQDN means we have to strip off the domain name portion.
         We do this by determining our own domain name, then searching the string
         for this domain name and removing it.
       */
#ifdef HAVE_GETHOSTNAME
      if (niquery->flags & ARES_NI_NOFQDN)
        {
           char buf[255];
           char *domain;
           gethostname(buf, 255);
           if ((domain = strchr(buf, '.')))
             {
               char *end = ares_striendstr(host->h_name, domain);
               if (end)
                 *end = 0;
             }
        }
#endif
      niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts, (char *)(host->h_name),
                        service);
      return;
    }
  /* We couldn't find the host, but it's OK, we can use the IP */
  else if (status == ARES_ENOTFOUND && !(niquery->flags & ARES_NI_NAMEREQD))
    {
      char ipbuf[IPBUFSIZ];
      if (niquery->family == AF_INET)
        ares_inet_ntop(AF_INET, &niquery->addr.addr4.sin_addr, ipbuf, IPBUFSIZ);
      else
        {
          ares_inet_ntop(AF_INET6, &niquery->addr.addr6.sin6_addr, ipbuf, IPBUFSIZ);
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
          append_scopeid(&niquery->addr.addr6, niquery->flags, ipbuf, sizeof(ipbuf));
#endif
        }
      /* They want a service too */
      if (niquery->flags & ARES_NI_LOOKUPSERVICE)
        {
          if (niquery->family == AF_INET)
            service = lookup_service(niquery->addr.addr4.sin_port,
                                     niquery->flags, srvbuf, sizeof(srvbuf));
          else
            service = lookup_service(niquery->addr.addr6.sin6_port,
                                     niquery->flags, srvbuf, sizeof(srvbuf));
        }
      niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts, ipbuf, service);
      return;
    }
  niquery->callback(niquery->arg, status, niquery->timeouts, NULL, NULL);
  free(niquery);
}
static void
host_cb(void *arg, int status, int timeouts, struct hostent *hostent)
{
    PyGILState_STATE gstate = PyGILState_Ensure();
    char ip[INET6_ADDRSTRLEN];
    char **ptr;
    PyObject *callback, *dns_name, *errorno, *dns_aliases, *dns_addrlist, *dns_result, *tmp, *result;

    callback = (PyObject *)arg;
    ASSERT(callback);

    if (status != ARES_SUCCESS) {
        errorno = PyInt_FromLong((long)status);
        dns_result = Py_None;
        Py_INCREF(Py_None);
        goto callback;
    }

    dns_aliases = PyList_New(0);
    dns_addrlist = PyList_New(0);
    dns_result = PyStructSequence_New(&AresHostResultType);

    if (!(dns_aliases && dns_addrlist && dns_result)) {
        PyErr_NoMemory();
        PyErr_WriteUnraisable(Py_None);
        Py_XDECREF(dns_aliases);
        Py_XDECREF(dns_addrlist);
        Py_XDECREF(dns_result);
        errorno = PyInt_FromLong((long)ARES_ENOMEM);
        dns_result = Py_None;
        Py_INCREF(Py_None);
        goto callback;
    }

    for (ptr = hostent->h_aliases; *ptr != NULL; ptr++) {
        if (*ptr != hostent->h_name && strcmp(*ptr, hostent->h_name)) {
            tmp = Py_BuildValue("s", *ptr);
            if (tmp == NULL) {
                break;
            }
            PyList_Append(dns_aliases, tmp);
            Py_DECREF(tmp);
        }
    }
    for (ptr = hostent->h_addr_list; *ptr != NULL; ptr++) {
        if (hostent->h_addrtype == AF_INET) {
            ares_inet_ntop(AF_INET, *ptr, ip, INET_ADDRSTRLEN);
            tmp = Py_BuildValue("s", ip);
        } else if (hostent->h_addrtype == AF_INET6) {
            ares_inet_ntop(AF_INET6, *ptr, ip, INET6_ADDRSTRLEN);
            tmp = Py_BuildValue("s", ip);
        } else {
            continue;
        }
        if (tmp == NULL) {
            break;
        }
        PyList_Append(dns_addrlist, tmp);
        Py_DECREF(tmp);
    }
    dns_name = Py_BuildValue("s", hostent->h_name);

    PyStructSequence_SET_ITEM(dns_result, 0, dns_name);
    PyStructSequence_SET_ITEM(dns_result, 1, dns_aliases);
    PyStructSequence_SET_ITEM(dns_result, 2, dns_addrlist);
    errorno = Py_None;
    Py_INCREF(Py_None);

callback:
    result = PyObject_CallFunctionObjArgs(callback, dns_result, errorno, NULL);
    if (result == NULL) {
        PyErr_WriteUnraisable(callback);
    }
    Py_XDECREF(result);
    Py_DECREF(dns_result);
    Py_DECREF(errorno);

    Py_DECREF(callback);
    PyGILState_Release(gstate);
}
static void
query_a_cb(void *arg, int status,int timeouts, unsigned char *answer_buf, int answer_len)
{
    PyGILState_STATE gstate = PyGILState_Ensure();
    int parse_status;
    char ip[INET6_ADDRSTRLEN];
    char **ptr;
    struct hostent *hostent = NULL;
    PyObject *dns_result, *errorno, *tmp, *result, *callback;

    callback = (PyObject *)arg;
    ASSERT(callback);

    if (status != ARES_SUCCESS) {
        errorno = PyInt_FromLong((long)status);
        dns_result = Py_None;
        Py_INCREF(Py_None);
        goto callback;
    }

    parse_status = ares_parse_a_reply(answer_buf, answer_len, &hostent, NULL, NULL);
    if (parse_status != ARES_SUCCESS) {
        errorno = PyInt_FromLong((long)parse_status);
        dns_result = Py_None;
        Py_INCREF(Py_None);
        goto callback;
    }

    dns_result = PyList_New(0);
    if (!dns_result) {
        PyErr_NoMemory();
        PyErr_WriteUnraisable(Py_None);
        errorno = PyInt_FromLong((long)ARES_ENOMEM);
        dns_result = Py_None;
        Py_INCREF(Py_None);
        goto callback;
    }

    for (ptr = hostent->h_addr_list; *ptr != NULL; ptr++) {
        ares_inet_ntop(hostent->h_addrtype, *ptr, ip, sizeof(ip));
        tmp = Py_BuildValue("s", ip);
        if (tmp == NULL) {
            break;
        }
        PyList_Append(dns_result, tmp);
        Py_DECREF(tmp);
    }
    errorno = Py_None;
    Py_INCREF(Py_None);

callback:
    result = PyObject_CallFunctionObjArgs(callback, dns_result, errorno, NULL);
    if (result == NULL) {
        PyErr_WriteUnraisable(callback);
    }
    Py_XDECREF(result);
    Py_DECREF(dns_result);
    Py_DECREF(errorno);
    if (hostent) {
        ares_free_hostent(hostent);
    }
    Py_DECREF(callback);
    PyGILState_Release(gstate);
}
Exemplo n.º 8
0
int uv_ip6_name(struct sockaddr_in6* src, char* dst, size_t size) {
  char* d = ares_inet_ntop(AF_INET6, &src->sin6_addr, dst, size);
  return d != dst;
}
Exemplo n.º 9
0
int uv_ip4_name(struct sockaddr_in* src, char* dst, size_t size) {
  const char* d = ares_inet_ntop(AF_INET, &src->sin_addr, dst, size);
  return d != dst;
}
Exemplo n.º 10
0
static const unsigned char *display_rr(const unsigned char *aptr,
                                       const unsigned char *abuf, int alen)
{
  const unsigned char *p;
  int type, dnsclass, ttl, dlen, status;
  long len;
  char addr[46];
  union {
    unsigned char * as_uchar;
             char * as_char;
  } name;

  /* Parse the RR name. */
  status = ares_expand_name(aptr, abuf, alen, &name.as_char, &len);
  if (status != ARES_SUCCESS)
    return NULL;
  aptr += len;

  /* Make sure there is enough data after the RR name for the fixed
   * part of the RR.
   */
  if (aptr + RRFIXEDSZ > abuf + alen)
    {
      ares_free_string(name.as_char);
      return NULL;
    }

  /* Parse the fixed part of the RR, and advance to the RR data
   * field. */
  type = DNS_RR_TYPE(aptr);
  dnsclass = DNS_RR_CLASS(aptr);
  ttl = DNS_RR_TTL(aptr);
  dlen = DNS_RR_LEN(aptr);
  aptr += RRFIXEDSZ;
  if (aptr + dlen > abuf + alen)
    {
      ares_free_string(name.as_char);
      return NULL;
    }

  /* Display the RR name, class, and type. */
  printf("\t%-15s.\t%d", name.as_char, ttl);
  if (dnsclass != C_IN)
    printf("\t%s", class_name(dnsclass));
  printf("\t%s", type_name(type));
  ares_free_string(name.as_char);

  /* Display the RR data.  Don't touch aptr. */
  switch (type)
    {
    case T_CNAME:
    case T_MB:
    case T_MD:
    case T_MF:
    case T_MG:
    case T_MR:
    case T_NS:
    case T_PTR:
      /* For these types, the RR data is just a domain name. */
      status = ares_expand_name(aptr, abuf, alen, &name.as_char, &len);
      if (status != ARES_SUCCESS)
        return NULL;
      printf("\t%s.", name.as_char);
      ares_free_string(name.as_char);
      break;

    case T_HINFO:
      /* The RR data is two length-counted character strings. */
      p = aptr;
      len = *p;
      if (p + len + 1 > aptr + dlen)
        return NULL;
      status = ares_expand_string(p, abuf, alen, &name.as_uchar, &len);
      if (status != ARES_SUCCESS)
        return NULL;
      printf("\t%s", name.as_char);
      ares_free_string(name.as_char);
      p += len;
      len = *p;
      if (p + len + 1 > aptr + dlen)
        return NULL;
      status = ares_expand_string(p, abuf, alen, &name.as_uchar, &len);
      if (status != ARES_SUCCESS)
        return NULL;
      printf("\t%s", name.as_char);
      ares_free_string(name.as_char);
      break;

    case T_MINFO:
      /* The RR data is two domain names. */
      p = aptr;
      status = ares_expand_name(p, abuf, alen, &name.as_char, &len);
      if (status != ARES_SUCCESS)
        return NULL;
      printf("\t%s.", name.as_char);
      ares_free_string(name.as_char);
      p += len;
      status = ares_expand_name(p, abuf, alen, &name.as_char, &len);
      if (status != ARES_SUCCESS)
        return NULL;
      printf("\t%s.", name.as_char);
      ares_free_string(name.as_char);
      break;

    case T_MX:
      /* The RR data is two bytes giving a preference ordering, and
       * then a domain name.
       */
      if (dlen < 2)
        return NULL;
      printf("\t%d", DNS__16BIT(aptr));
      status = ares_expand_name(aptr + 2, abuf, alen, &name.as_char, &len);
      if (status != ARES_SUCCESS)
        return NULL;
      printf("\t%s.", name.as_char);
      ares_free_string(name.as_char);
      break;

    case T_SOA:
      /* The RR data is two domain names and then five four-byte
       * numbers giving the serial number and some timeouts.
       */
      p = aptr;
      status = ares_expand_name(p, abuf, alen, &name.as_char, &len);
      if (status != ARES_SUCCESS)
        return NULL;
      printf("\t%s.\n", name.as_char);
      ares_free_string(name.as_char);
      p += len;
      status = ares_expand_name(p, abuf, alen, &name.as_char, &len);
      if (status != ARES_SUCCESS)
        return NULL;
      printf("\t\t\t\t\t\t%s.\n", name.as_char);
      ares_free_string(name.as_char);
      p += len;
      if (p + 20 > aptr + dlen)
        return NULL;
      printf("\t\t\t\t\t\t( %lu %lu %lu %lu %lu )",
             (unsigned long)DNS__32BIT(p), (unsigned long)DNS__32BIT(p+4),
             (unsigned long)DNS__32BIT(p+8), (unsigned long)DNS__32BIT(p+12),
             (unsigned long)DNS__32BIT(p+16));
      break;

    case T_TXT:
      /* The RR data is one or more length-counted character
       * strings. */
      p = aptr;
      while (p < aptr + dlen)
        {
          len = *p;
          if (p + len + 1 > aptr + dlen)
            return NULL;
          status = ares_expand_string(p, abuf, alen, &name.as_uchar, &len);
          if (status != ARES_SUCCESS)
            return NULL;
          printf("\t%s", name.as_char);
          ares_free_string(name.as_char);
          p += len;
        }
      break;

    case T_A:
      /* The RR data is a four-byte Internet address. */
      if (dlen != 4)
        return NULL;
      printf("\t%s", ares_inet_ntop(AF_INET,aptr,addr,sizeof(addr)));
      break;

    case T_AAAA:
      /* The RR data is a 16-byte IPv6 address. */
      if (dlen != 16)
        return NULL;
      printf("\t%s", ares_inet_ntop(AF_INET6,aptr,addr,sizeof(addr)));
      break;

    case T_WKS:
      /* Not implemented yet */
      break;

    case T_SRV:
      /* The RR data is three two-byte numbers representing the
       * priority, weight, and port, followed by a domain name.
       */

      printf("\t%d", DNS__16BIT(aptr));
      printf(" %d", DNS__16BIT(aptr + 2));
      printf(" %d", DNS__16BIT(aptr + 4));

      status = ares_expand_name(aptr + 6, abuf, alen, &name.as_char, &len);
      if (status != ARES_SUCCESS)
        return NULL;
      printf("\t%s.", name.as_char);
      ares_free_string(name.as_char);
      break;

    case T_NAPTR:

      printf("\t%d", DNS__16BIT(aptr)); /* order */
      printf(" %d\n", DNS__16BIT(aptr + 2)); /* preference */

      p = aptr + 4;
      status = ares_expand_string(p, abuf, alen, &name.as_uchar, &len);
      if (status != ARES_SUCCESS)
        return NULL;
      printf("\t\t\t\t\t\t%s\n", name.as_char);
      ares_free_string(name.as_char);
      p += len;

      status = ares_expand_string(p, abuf, alen, &name.as_uchar, &len);
      if (status != ARES_SUCCESS)
        return NULL;
      printf("\t\t\t\t\t\t%s\n", name.as_char);
      ares_free_string(name.as_char);
      p += len;

      status = ares_expand_string(p, abuf, alen, &name.as_uchar, &len);
      if (status != ARES_SUCCESS)
        return NULL;
      printf("\t\t\t\t\t\t%s\n", name.as_char);
      ares_free_string(name.as_char);
      p += len;

      status = ares_expand_string(p, abuf, alen, &name.as_uchar, &len);
      if (status != ARES_SUCCESS)
        return NULL;
      printf("\t\t\t\t\t\t%s", name.as_char);
      ares_free_string(name.as_char);
      break;


    default:
      printf("\t[Unknown RR; cannot parse]");
      break;
    }
  printf("\n");

  return aptr + dlen;
}
Exemplo n.º 11
0
/**
 * The desired output for this method is that we set "ret_buf" to
 * something like:
 *
 * 192.168.0.1,dns01.my.domain,fe80::200:f8ff:fe21:67cf
 *
 * The only ordering requirement is that primary servers are listed
 * before secondary. There is no requirement that IPv4 addresses should
 * necessarily be before IPv6.
 *
 * Note that ret_size should ideally be big enough to hold around
 * 2-3 IPv4 and 2-3 IPv6 addresses.
 *
 * Finally, we need to return the total number of DNS servers located.
 */
static int get_iphlpapi_dns_info (char *ret_buf, size_t ret_size)
{
  const size_t  ipv4_size = INET_ADDRSTRLEN  + 1;  /* +1 for ',' at end */
  const size_t  ipv6_size = INET6_ADDRSTRLEN + 12; /* +12 for "%0123456789," at end */
  size_t        left = ret_size;
  char         *ret  = ret_buf;
  int           count = 0;

  /* Use the GetAdaptersAddresses method if it's available, otherwise
     fall back to GetNetworkParams. */
  if (ares_fpGetAdaptersAddresses != ZERO_NULL)
  {
    const ULONG            working_buf_size = 15000;
    IP_ADAPTER_ADDRESSES   *pFirstEntry = NULL;
    IP_ADAPTER_ADDRESSES   *pEntry = NULL;
    ULONG                  bufSize = 0;
    ULONG                  result = 0;

    /* According to MSDN, the recommended way to do this is to use a temporary
       buffer of 15K, to "dramatically reduce the chance that the GetAdaptersAddresses
       method returns ERROR_BUFFER_OVERFLOW" */
    pFirstEntry  = ( IP_ADAPTER_ADDRESSES * ) malloc( working_buf_size );
    bufSize = working_buf_size;
    if( !pFirstEntry )
      return 0;

    /* Call the method one time */
    result = ( *ares_fpGetAdaptersAddresses )( AF_UNSPEC, 0, 0, pFirstEntry, &bufSize );
    if( result == ERROR_BUFFER_OVERFLOW )
    {
      /* Reallocate, bufSize should now be set to the required size */
      pFirstEntry = ( IP_ADAPTER_ADDRESSES * ) realloc( pFirstEntry, bufSize );
      if( !pFirstEntry )
        return 0;

      /* Call the method a second time */
      result = ( *ares_fpGetAdaptersAddresses )( AF_UNSPEC, 0, 0, pFirstEntry, &bufSize );
      if( result == ERROR_BUFFER_OVERFLOW )
      {
        /* Reallocate, bufSize should now be set to the required size */
        pFirstEntry = ( IP_ADAPTER_ADDRESSES * ) realloc( pFirstEntry, bufSize );
        if( !pFirstEntry )
          return 0;

        /* Call the method a third time. The maximum number of times we're going to do
           this is 3. Three shall be the number thou shalt count, and the number of the
           counting shall be three.  Five is right out. */
        result = ( *ares_fpGetAdaptersAddresses )( AF_UNSPEC, 0, 0, pFirstEntry, &bufSize );
      }
    }

    /* Check the current result for failure */
    if( result != ERROR_SUCCESS )
    {
      free( pFirstEntry );
      return 0;
    }

    /* process the results */
    for( pEntry = pFirstEntry ; pEntry != NULL ; pEntry = pEntry->Next )
    {
      IP_ADAPTER_DNS_SERVER_ADDRESS* pDNSAddr = pEntry->FirstDnsServerAddress;
      for( ; pDNSAddr != NULL ; pDNSAddr = pDNSAddr->Next )
      {
        struct sockaddr *pGenericAddr = pDNSAddr->Address.lpSockaddr;
        size_t stringlen = 0;

        if( pGenericAddr->sa_family == AF_INET && left > ipv4_size )
        {
          /* Handle the v4 case */
          struct sockaddr_in *pIPv4Addr = ( struct sockaddr_in * ) pGenericAddr;
          ares_inet_ntop( AF_INET, &pIPv4Addr->sin_addr, ret, ipv4_size - 1 ); /* -1 for comma */

          /* Append a comma to the end, THEN NULL. Should be OK because we
             already tested the size at the top of the if statement. */
          stringlen = strlen( ret );
          ret[ stringlen ] = ',';
          ret[ stringlen + 1 ] = '\0';
          ret += stringlen + 1;
          left -= ret - ret_buf;
          ++count;
        }
        else if( pGenericAddr->sa_family == AF_INET6 && left > ipv6_size )
        {
          /* Handle the v6 case */
          struct sockaddr_in6 *pIPv6Addr = ( struct sockaddr_in6 * ) pGenericAddr;
          ares_inet_ntop( AF_INET6, &pIPv6Addr->sin6_addr, ret, ipv6_size - 1 ); /* -1 for comma */

          /* Append a comma to the end, THEN NULL. Should be OK because we
             already tested the size at the top of the if statement. */
          stringlen = strlen( ret );
          ret[ stringlen ] = ',';
          ret[ stringlen + 1 ] = '\0';
          ret += stringlen + 1;
          left -= ret - ret_buf;
          ++count;

          /* NB on Windows this also returns stuff in the fec0::/10 range,
             seems to be hard-coded somehow. Do we need to ignore them? */
        }
      }
    }

    if( pFirstEntry )
      free( pFirstEntry );
    if (ret > ret_buf)
      ret[-1] = '\0';
    return count;
  }
  else
  {
    FIXED_INFO    *fi, *newfi;
    DWORD          size = sizeof (*fi);
    IP_ADDR_STRING *ipAddr;
    int            i;
    int            debug  = 0;
    HRESULT        res;

    fi = malloc(size);
    if (!fi)
      return 0;

    res = (*ares_fpGetNetworkParams) (fi, &size);
    if ((res != ERROR_BUFFER_OVERFLOW) && (res != ERROR_SUCCESS))
      goto quit;

    newfi = realloc(fi, size);
    if (!newfi)
      goto quit;

    fi = newfi;
    res = (*ares_fpGetNetworkParams) (fi, &size);
    if (res != ERROR_SUCCESS)
      goto quit;

    if (debug)
    {
      printf ("Host Name: %s\n", fi->HostName);
      printf ("Domain Name: %s\n", fi->DomainName);
      printf ("DNS Servers:\n"
              "    %s (primary)\n", fi->DnsServerList.IpAddress.String);
    }
    if (strlen(fi->DnsServerList.IpAddress.String) > 0 &&
        inet_addr(fi->DnsServerList.IpAddress.String) != INADDR_NONE &&
        left > ipv4_size)
    {
      ret += sprintf (ret, "%s,", fi->DnsServerList.IpAddress.String);
      left -= ret - ret_buf;
      ++count;
    }

    for (i = 0, ipAddr = fi->DnsServerList.Next; ipAddr && left > ipv4_size;
         ipAddr = ipAddr->Next, i++)
    {
      if (inet_addr(ipAddr->IpAddress.String) != INADDR_NONE)
      {
         ret += sprintf (ret, "%s,", ipAddr->IpAddress.String);
         left -= ret - ret_buf;
         ++count;
      }
      if (debug)
         printf ("    %s (secondary %d)\n", ipAddr->IpAddress.String, i+1);
    }

quit:
    if (fi)
      free(fi);

    if (debug && left <= ipv4_size)
      printf ("Too many nameservers. Truncating to %d addressess", count);
    if (ret > ret_buf)
      ret[-1] = '\0';
    return count;
  }
}