/*
 * Generalized adaptation to ZMailer libc fill-in use by
 * Matti Aarnio <mea@nic.funet.fi> 1997
 *
 * The original version was a bit too much Linux specific...
 */

/*
%%% copyright-cmetz-96
This software is Copyright 1996-1997 by Craig Metz, All Rights Reserved.
The Inner Net License Version 2 applies to this software.
You should have received a copy of the license with this software. If
you didn't get a copy, you may request one from <license@inner.net>.

*/
/* getaddrinfo() v1.22; v1.26, v1.27(w/o debug code) */

/* To do what POSIX says, even when it's broken, define: */
/* #define BROKEN_LIKE_POSIX 1 */
/* Note: real apps will break if you define this, while nothing other than a
   conformance test suite should have a problem with it undefined */

#include "hostenv.h"
#include <sys/types.h>
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <sys/socket.h>

#include <stdio.h>
#include <string.h>
#include <sys/utsname.h>
#include <sys/un.h>

#include <netinet/in.h>
#ifdef HAVE_NETINET_IN6_H
# include <netinet/in6.h>
#endif
#ifdef HAVE_NETINET6_IN6_H
# include <netinet6/in6.h>
#endif
#ifdef HAVE_LINUX_IN6_H
# include <linux/in6.h>
#endif

#include <netdb.h>
#if !defined(EAI_AGAIN) || !defined(AI_NUMERICHOST)
#include "netdb6.h"
#endif

#include <arpa/nameser.h>
#include <resolv.h>

extern int h_errno;

#ifndef AF_LOCAL
#define AF_LOCAL AF_UNIX
#endif /* AF_LOCAL */
#ifndef PF_LOCAL
#define PF_LOCAL PF_UNIX
#endif /* PF_LOCAL */
#ifndef UNIX_PATH_MAX
#define UNIX_PATH_MAX 108
#endif /* UNIX_PATH_MAX */

#undef AF_LOCAL /* We DO NOT do AF_LOCAL/AF_UNIX stuff... */

#include <ctype.h>
#include "libc.h"

#ifndef T_AAAA
# define T_AAAA 28 /* IPv6 address record.  Codeless w/o INET6 */
#endif

#define GAIH_OKIFUNSPEC 0x0100
#define GAIH_EAI        ~(GAIH_OKIFUNSPEC)

static struct addrinfo nullreq =
{ 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL };

struct gaih_service {
  char *name;
  int num;
};

struct gaih_servtuple {
  struct gaih_servtuple *next;
  int socktype;
  int protocol;
  int port;
};

static struct gaih_servtuple nullserv = {
  NULL, 0, 0, 0
};

struct gaih_addrtuple {
  struct gaih_addrtuple *next;
  int family;
  char addr[16];
  char *cname;
};

struct gaih_typeproto {
  int socktype;
  int protocol;
  char *name;
};

static int hosttable_lookup_addr __((const char *name,
				     const struct addrinfo *req,
				     struct gaih_addrtuple **pat));

static int
hosttable_lookup_addr(name, req, pat)
const char *name;
const struct addrinfo *req;
struct gaih_addrtuple **pat;
{
  FILE *f;
  char buffer[1024];
  char *c, *c2;
  int rval = 1;
  char *prevcname = NULL;

  f = fopen("/etc/hosts", "r");
  if (f == NULL)
    return -(EAI_SYSTEM);

  while (fgets(buffer, sizeof(buffer), f)) {
    c = strchr(buffer, '#');
    if (c != NULL)
      *c = 0;

    c = buffer;
    while (*c && !isspace(*c)) c++;
    if (!*c)
      continue;

    *(c++) = 0;

    while (*c && isspace(*c)) c++;
    if (!*c)
      continue;

    c2 = strstr(c, name);
    if (c2 == NULL)
      continue;

    if (*(c2 - 1) && !isspace(*(c2 - 1)))
      continue;

    c2 += strlen(name);
    if (*c2 && !isspace(*c2))
      continue;

    c2 = c;
    while (*c2 && !isspace(*c2)) c2++;
    if (!*c2)
      continue;
    *c2 = 0;

    if (*pat == NULL) {
      *pat = (void*)malloc(sizeof(struct gaih_addrtuple));
      if (*pat == NULL)
	return -(EAI_MEMORY);
    }

    memset(*pat, 0, sizeof(struct gaih_addrtuple));

    if (!req->ai_family || (req->ai_family == AF_INET))
      if (inet_pton(AF_INET, buffer, (void*)((*pat)->addr)) > 0) {
	(*pat)->family = AF_INET;
	goto build;
      }

#if defined(INET6) && defined(AF_INET6)
    if (!req->ai_family || (req->ai_family == AF_INET6))
      if (inet_pton(AF_INET6, buffer, (void*)((*pat)->addr)) > 0) {
	(*pat)->family = AF_INET6;
	goto build;
      }
#endif /* INET6 */

    continue;

build:
    if (req->ai_flags & AI_CANONNAME) {
      if (prevcname && !strcmp(prevcname, c))
	(*pat)->cname = prevcname;
      else
	prevcname = (*pat)->cname = strdup(c);
    }

    pat = &((*pat)->next);

    rval = 0;
  }

  fclose(f);
  return (rval);
}

#ifndef HFIXEDSZ
#define HFIXEDSZ 12
#endif
#ifndef RRHEADER_SZ
#define RRHEADER_SZ 10
#endif

static int resolver_lookup_addr __((const char *name, int type,
				    const struct addrinfo *req,
				    struct gaih_addrtuple **pat,
				    FILE *vlog));

static int
resolver_lookup_addr(name, type, req, pat, vlog)
const char *name;
int type;
const struct addrinfo *req;
struct gaih_addrtuple **pat;
FILE *vlog;
{
  char answer[PACKETSZ];
  int answerlen;
  char dn[/* MAXDNAME */ 128];
  char *prevcname = NULL;
  char *p, *ep;
  int answers, qdcount, i, j;
  int rclass;

  answerlen = res_search(name, C_IN, type, (void*)answer, sizeof(answer));
  if (answerlen < 0) {
    switch(h_errno) {
#ifdef NETDB_INTERNAL
    case NETDB_INTERNAL:
      if (vlog)
	fprintf(vlog,"res_search() yields NETDB_INTERNAL error for lookup of name='%s', type=%d\n",name,type);
      return -(EAI_SYSTEM);
#endif
    case HOST_NOT_FOUND:
      return 1;
    case TRY_AGAIN:
      return -(EAI_AGAIN); /* XXX */
    case NO_RECOVERY:
      if (vlog) fprintf(vlog, "res_search('%s',C_IN,type=%d) -> NO_RECOVERY error\n", name, type);
      return -(EAI_FAIL);
    case NO_DATA:
      return 1;
    default:
      if (vlog) fprintf(vlog, "res_search() yields unknown h_errno value: %d\n", h_errno);
      return -(EAI_FAIL);
    }
  }

  p  = answer;
  ep = answer + answerlen;

  if (answerlen < HFIXEDSZ) {
    if (vlog)
      fprintf(vlog,"res_search() yielded answer with too small reply: %d ( < 10 )\n", answerlen);
    return -(EAI_FAIL);
  } else {
    HEADER *h = (HEADER *)p; /* This is aligned block, anything after this
				may be nonaligned */
    qdcount = ntohs(h->qdcount);
    answers = ntohs(h->ancount);
    if (!h->qr || (h->opcode != QUERY) || (qdcount != 1) || !answers) {
      if (vlog) fprintf(vlog, "eaifail%d\n",__LINE__);
      return -(EAI_FAIL);
    }
  }
  p += HFIXEDSZ;

  dn[0] = 0;
  /* Question playback analysis */
  for (; qdcount > 0; --qdcount) {
    int qt, qc;
    i = dn_expand((void*)answer, (void*)ep, (void*)p, dn, sizeof(dn));
#if 0
#if	defined(BIND_VER) && (BIND_VER >= 473)
#else	/* !defined(BIND_VER) || (BIND_VER < 473) */
    i = dn_skip((const unsigned char*)p);
#endif	/* defined(BIND_VER) && (BIND_VER >= 473) */
#endif
    p += i;
    if (i < 0) {
      if (vlog) fprintf(vlog, "eaifail%d\n",__LINE__);
      return -(EAI_FAIL);
    }
    qt = _getshort(p); p += 2;
    qc = _getshort(p); p += 2;
#if 0
    if (qt != type || qc != C_IN) {
      if (vlog) fprintf(vlog, "eaifail%d\n",__LINE__);
      return -(EAI_FAIL);
    }
#endif
    if (qc != C_IN)
      return -(EAI_FAIL);
  }

  if (vlog)
    fprintf(vlog,"resolver(): question skipped, dn='%s', first answer at p=%d  answers=%d\n", dn, (int)(p-answer), (int)answers);

  /* Answer analysis */
  for ( ; answers > 0; --answers) {
    i = dn_expand((void*)answer, (void*)ep, (void*)p, dn, sizeof(dn));
    if (i < 0) {
      if (vlog)
	fprintf(vlog, "resolver() dn_expand() fail; eof-p=%d, p-start=%d\n",
		(int)(ep-p), (int)(p-answer));
      return -(EAI_FAIL);
    }
    p += i;

    if (p + RRHEADER_SZ > ep) { /* Too little data! */
      if (vlog) fprintf(vlog, "resolver(): Out of data in reply [1]\n");
      return -(EAI_FAIL);
    } else {
      j = _getshort(p);      p += 2; /* type */
      rclass = _getshort(p); p += 2; /* class */
      if (rclass != C_IN) {
	if (vlog) fprintf(vlog, "resolver() reply block class info != C_IN: %d\n", rclass );
	return -(EAI_FAIL);
      }
      p += 4;              /* TTL  */
      i = _getshort(p); p += 2;      /* size */
    }

    if (p + i > ep) {
      if (vlog) fprintf(vlog, "resolver(): Out of data in reply [2]\n");
      return -(EAI_FAIL);
    }

    if (j == type) {
      while (*pat)
	pat = &((*pat)->next);

      switch (type) {
        case T_A:
	  if (i != 4) {
	    if (vlog) fprintf(vlog, "eaifail@%s:%d; A size = %d != 4\n",__FILE__, __LINE__, i);
	    return -(EAI_FAIL);
	  }

	  *pat = (void*)malloc(sizeof(struct gaih_addrtuple));
	  if (*pat == NULL)
	    return -(EAI_MEMORY);
	  memset(*pat, 0, sizeof(struct gaih_addrtuple));

	  (*pat)->family = AF_INET;
	  break;

#if defined(INET6) && defined(AF_INET6)
        case T_AAAA:
	  if (i != 16) {
	    if (vlog) fprintf(vlog, "eaifail@%s:%d; AAAA size = %d != 16\n",__FILE__,__LINE__, i);
	    return -(EAI_FAIL);
	  }

	  *pat = (void*)malloc(sizeof(struct gaih_addrtuple));
	  if (*pat == NULL)
	    return -(EAI_MEMORY);
	  memset(*pat, 0, sizeof(struct gaih_addrtuple));

	  (*pat)->family = AF_INET6;
	  break;
#endif /* INET6 */
        default:
	  p += i;
	  continue; /* Skip non T_A / T_AAAA entries */
	  break;
      }
      memcpy((*pat)->addr, p, i);
    
      if (req->ai_flags & AI_CANONNAME) {
	if (prevcname && !strcmp(prevcname, dn))
	  (*pat)->cname = prevcname;
	else
	  prevcname = (*pat)->cname = strdup(dn);
      }
    }
    p += i;
  }

  return 0;
}

#ifdef AF_LOCAL
static int gaih_local __((const char *name, const struct gaih_service *service,
			  const struct addrinfo *req, struct addrinfo **pai,
			  FILE *vlog));

static int
gaih_local(name, service, req, pai, vlog)
const char *name;
const struct gaih_service *service;
const struct addrinfo *req;
struct addrinfo **pai;
FILE *vlog;
{
  struct utsname utsname;
  int newsize;
  struct sockaddr_un *saun;

  if (name || (req->ai_flags & AI_CANONNAME))
    if (uname(&utsname) != 0) {
      return -(EAI_SYSTEM);
    }

  if (name != NULL) {
    if (strcmp(name, "localhost") != 0 &&
	strcmp(name, "local")     != 0 &&
	strcmp(name, "unix")      != 0 &&
	strcmp(name, utsname.nodename) != 0)
      return (GAIH_OKIFUNSPEC | -(EAI_NONAME));
  }

  /* I am conservative, and suspect (seriously) compiler qualities
     in handling complex convoluted "t:a?b" expressions... [mea] */
  newsize  = sizeof(struct addrinfo) + sizeof(struct sockaddr_un);
  newsize += ((req->ai_flags & AI_CANONNAME) ?
	      (strlen(utsname.nodename) + 1): 0);
  *pai = (void*)malloc(newsize);
  if (*pai == NULL)
    return -(EAI_MEMORY);

  (*pai)->ai_next     = NULL;
  (*pai)->ai_flags    = req->ai_flags;
  (*pai)->ai_family   = AF_LOCAL;
  (*pai)->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM;
  (*pai)->ai_protocol = req->ai_protocol;
  (*pai)->ai_addrlen  = sizeof(struct sockaddr_un);
  (*pai)->ai_addr     = (void *)((char *)(*pai) + sizeof(struct addrinfo));
  saun = (struct sockaddr_un *) (*pai)->ai_addr;
#if HAVE_SA_LEN
  saun->sun_len = sizeof(struct sockaddr_un);
#endif /* SALEN */
  saun->sun_family = AF_LOCAL;
  memset(saun->sun_path, 0, UNIX_PATH_MAX);
  if (service != NULL) {
    char *c = strchr(service->name, '/');
    if (c != NULL) {
      if (strlen(service->name) >= sizeof(saun->sun_path))
        return (GAIH_OKIFUNSPEC | -(EAI_SERVICE));
      strcpy(saun->sun_path, service->name);
    } else {
      if (strlen(P_tmpdir "/") + 1 + strlen(service->name) >= sizeof(saun->sun_path))
        return (GAIH_OKIFUNSPEC | -(EAI_SERVICE));
      strcpy(saun->sun_path, P_tmpdir "/");
      strcat(saun->sun_path, service->name);
    }
  } else {
    if (!tmpnam(saun->sun_path))
      return (-(EAI_SYSTEM) | GAIH_OKIFUNSPEC);
  }
  if (req->ai_flags & AI_CANONNAME) {
    (*pai)->ai_canonname = ((char *)(*pai) + sizeof(struct addrinfo) +
			    sizeof(struct sockaddr_un));
    strcpy((*pai)->ai_canonname, utsname.nodename);
  } else
    (*pai)->ai_canonname = NULL;
  return 0;
}
#endif

static struct gaih_typeproto gaih_inet_typeproto[] = {
  { 0, 0, NULL },
  { SOCK_STREAM, IPPROTO_TCP, "tcp" },
  { SOCK_DGRAM, IPPROTO_UDP, "udp" },
  { 0, 0, NULL }
};

static int gaih_inet_serv __((char *servicename, struct gaih_typeproto *tp,
			      struct gaih_servtuple **st));

static int
gaih_inet_serv(servicename, tp, st)
char *servicename;
struct gaih_typeproto *tp;
struct gaih_servtuple **st;
{
  struct servent *s;

  s = getservbyname(servicename, tp->name);
  if (s == NULL)
    return (GAIH_OKIFUNSPEC | -(EAI_SERVICE));

  *st = (void*)malloc(sizeof(struct gaih_servtuple));
  if (*st == NULL)
    return -(EAI_MEMORY);

  (*st)->next     = NULL;
  (*st)->socktype = tp->socktype;
  (*st)->protocol = tp->protocol;
  (*st)->port     = s->s_port;

  return 0;
}

static int gaih_inet __((const char *name, const struct gaih_service *service,
			 const struct addrinfo *req, struct addrinfo **pai,
			 FILE *vlog));

static int
gaih_inet(name, service, req, pai, vlog)
const char *name;
const struct gaih_service *service;
const struct addrinfo *req;
struct addrinfo **pai;
FILE *vlog;
{
  struct gaih_typeproto *tp = gaih_inet_typeproto;
  struct gaih_servtuple *st = &nullserv;
  struct gaih_addrtuple *at = NULL;
  int i;

  if (req->ai_protocol || req->ai_socktype) {
    for (tp++; tp->name &&
	   ((req->ai_socktype != tp->socktype) || !req->ai_socktype) && 
	   ((req->ai_protocol != tp->protocol) || !req->ai_protocol); tp++);
    if (!tp->name) {
      if (req->ai_socktype)
	return (GAIH_OKIFUNSPEC | -(EAI_SOCKTYPE));
      else
	return (GAIH_OKIFUNSPEC | -(EAI_SERVICE));
    }
  }

  if (service) {
    if (service->num < 0) {
      if (tp->name) {
	i = gaih_inet_serv(service->name, tp, &st);
	if (i != 0)
	  return i;
      } else {
	struct gaih_servtuple **pst = &st;
	for (tp++; tp->name; tp++) {
	  i = gaih_inet_serv(service->name, tp, pst);
	  if (i != 0) {
	    if (i & GAIH_OKIFUNSPEC)
	      continue;
	    goto ret;
	  }
	  pst = &((*pst)->next);
	}
	if (st == &nullserv) {
	  i = (GAIH_OKIFUNSPEC | -(EAI_SERVICE));
	  goto ret;
	}
      }
    } else {
      st = (void*)malloc(sizeof(struct gaih_servtuple));
      if (st == NULL)
	return -(EAI_MEMORY);

      st->next     = NULL;
      st->socktype = tp->socktype;
      st->protocol = tp->protocol;
      st->port     = htons(service->num);
    }
  }

  if (!name) {
    at = (void*)malloc(sizeof(struct gaih_addrtuple));
    if (at == NULL) {
      i = -(EAI_MEMORY);
      goto ret;
    }

    memset(at, 0, sizeof(struct gaih_addrtuple));

#if defined(INET6) && defined(AF_INET6)
    at->next = (void*)malloc(sizeof(struct gaih_addrtuple));
    if (at->next == NULL) {
      i = -(EAI_MEMORY);
      goto ret;
    }

    memset(at->next, 0, sizeof(struct gaih_addrtuple));
    at->next->family = AF_INET;

    at->family = AF_INET6;
#else
    at->family = AF_INET;
#endif /* INET6 */

    goto build;
  }

  if (!req->ai_family || (req->ai_family == AF_INET)) {
    struct in_addr in_addr;
    if (inet_pton(AF_INET, name, (void*)&in_addr) > 0) {
      at = (void*)malloc(sizeof(struct gaih_addrtuple));
      if (at == NULL)
	return -(EAI_MEMORY);
      
      memset(at, 0, sizeof(struct gaih_addrtuple));
      
      at->family = AF_INET;
      memcpy(at->addr, &in_addr, sizeof(struct in_addr));
      goto build;
    }
  }

#if defined(INET6) && defined(AF_INET6)
  if (!req->ai_family || (req->ai_family == AF_INET6)) {
    struct in6_addr in6_addr;
    if (inet_pton(AF_INET6, name, (void*)&in6_addr) > 0) {
      if (!(at = (void*)malloc(sizeof(struct gaih_addrtuple))))
	return -(EAI_MEMORY);
      
      memset(at, 0, sizeof(struct gaih_addrtuple));
      
      at->family = AF_INET6;
      memcpy(at->addr, &in6_addr, sizeof(struct in6_addr));
      goto build;
    }
  }
#endif /* INET6 */

  if ((req->ai_flags & AI_NUMERICHOST) == 0) {
    i = hosttable_lookup_addr(name, req, &at);
if (vlog)
  fprintf(vlog,"hosttable_lookup_addr(name='%s') returns %d\n", name, i);

    if (i < 0)
      goto ret;
    if (i == 0)
      goto build;

#if defined(INET6) && defined(AF_INET6)
    if (!req->ai_family || (req->ai_family == AF_INET6)) {
      i = resolver_lookup_addr(name, T_AAAA, req, &at, vlog);
if (vlog)
  fprintf(vlog,"resolver_lookup_addr(name='%s', T_AAAA) returns %d\n",name, i);
      if (i < 0)
	goto ret;
    }
#endif /* INET6 */
    if (!req->ai_family || (req->ai_family == AF_INET)) {
      i = resolver_lookup_addr(name, T_A, req, &at, vlog);
if (vlog)
  fprintf(vlog,"resolver_lookup_addr(name='%s', T_A) returns %d\n",name, i);
      if (i < 0)
	goto ret;
    }

    if (i == 0)
      goto build;
  }

  if (at == NULL)
    return (GAIH_OKIFUNSPEC | -(EAI_NONAME));

build:
  {
    char *prevcname = NULL;
    struct gaih_servtuple *st2;
    struct gaih_addrtuple *at2 = at;
    int j;

    while (at2 != NULL) {
      if (req->ai_flags & AI_CANONNAME) {
	if (at2->cname != NULL)
	  j = strlen(at2->cname) + 1;
	else
	  if (name)
	    j = strlen(name) + 1;
	  else
	    j = 2;
      } else
	j = 0;

#if defined(INET6) && defined(AF_INET6)
      if (at2->family == AF_INET6)
	i = sizeof(struct sockaddr_in6);
      else
#endif /* INET6 */
	i = sizeof(struct sockaddr_in);

      st2 = st;
      while (st2) {

	*pai = (void*)malloc(sizeof(struct addrinfo) + i + j);
	if (*pai == NULL) {
	  i = -(EAI_MEMORY);
	  goto ret;
	}
	memset(*pai, 0, sizeof(struct addrinfo) + i + j);

	(*pai)->ai_flags     = req->ai_flags;
	(*pai)->ai_family    = at2->family;
	(*pai)->ai_socktype  = st2->socktype;
	(*pai)->ai_protocol  = st2->protocol;
	(*pai)->ai_addrlen   = i;
	(*pai)->ai_addr      = (void*)((char*)(*pai)+sizeof(struct addrinfo));

#if defined(INET6) && defined(AF_INET6)
	if (at2->family == AF_INET6) {
	  struct sockaddr_in6 *si6;
	  si6 = (struct sockaddr_in6 *) (*pai)->ai_addr;
#ifdef HAVE_SA_LEN
	  si6->sin6_len      = i;
#endif /* SALEN */
	  si6->sin6_family   = at2->family;
	  si6->sin6_flowinfo = 0;
	  si6->sin6_port     = st2->port;
	  memcpy(&si6->sin6_addr, at2->addr, sizeof(struct in6_addr));
	} else
#endif /* INET6 */
	  {
	    struct sockaddr_in  *si4;
	    si4 = (struct sockaddr_in *) (*pai)->ai_addr;
#ifdef HAVE_SA_LEN
	    si4->sin_len     = i;
#endif /* SALEN */
	    si4->sin_family  = at2->family;
	    si4->sin_port    = st2->port;
	    memcpy(&si4->sin_addr, at2->addr, sizeof(struct in_addr));
	  }

	if (j != 0) {
	  (*pai)->ai_canonname = (char *)(*pai) + sizeof(struct addrinfo) + i;
	  if (at2->cname != NULL) {
	    strcpy((*pai)->ai_canonname, at2->cname);
	    if (prevcname != at2->cname) {
	      if (prevcname != NULL)
		free(prevcname);
	      prevcname = at2->cname;
	    }
	  } else
	    strcpy((*pai)->ai_canonname, name ? name : "*");
	}

	pai = &((*pai)->ai_next);

	st2 = st2->next;
      }
      at2 = at2->next;
    }
  }

  i = 0;

ret:
  if (st != &nullserv) {
    struct gaih_servtuple *st2 = st;
    while (st != NULL) {
      st2 = st->next;
      free(st);
      st = st2;
    }
  }
  if (at) {
    struct gaih_addrtuple *at2 = at;
    while (at != NULL) {
      at2 = at->next;
      free(at);
      at = at2;
    }
  }
  return i;
}

struct gaih {
  int family;
  int (*gaih) __((const char *name, const struct gaih_service *service,
		  const struct addrinfo *req, struct addrinfo **pai,
		  FILE *vlog));
};

static struct gaih gaih[] = {
  { PF_INET,   gaih_inet  },
#if defined(INET6) && defined(AF_INET6)
  { PF_INET6,  gaih_inet  },
#endif /* INET6 */
#ifdef AF_LOCAL
  { PF_LOCAL,  gaih_local },
#endif
  { PF_UNSPEC, NULL }
};

int
_getaddrinfo_(name, service, req, pai, vlog)
const char		*name;
const char		*service;
const struct addrinfo	*req;
struct addrinfo		**pai;
FILE *vlog;
{
  int i = 0, j = 0;
  int anyok;
  struct addrinfo *p = NULL, **end;
  struct gaih *g = gaih, *pg = NULL;
  struct gaih_service gaih_service, *pservice;

  if (name && (name[0] == '*') && !name[1])
    name = NULL;

  if (service && (service[0] == '*') && !service[1])
    service = NULL;

#ifdef BROKEN_LIKE_POSIX
  if (!name && !service)
    return EAI_NONAME;
#endif /* BROKEN_LIKE_POSIX */

  if (!req)
    req = &nullreq;

  if (req->ai_flags & ~(AI_CANONNAME | AI_PASSIVE | AI_NUMERICHOST))
    return EAI_BADFLAGS;

#ifdef BROKEN_LIKE_POSIX
  if ((req->ai_flags & AI_CANONNAME) && !name)
    return EAI_BADFLAGS;
#endif

  if (service && *service) {
    char *c;
    gaih_service.name = (void *)service;
    gaih_service.num = strtoul(service, &c, 10);
    if (*c)
      gaih_service.num = -1;
#ifdef BROKEN_LIKE_POSIX
    else
      if (!req->ai_socktype)
	return EAI_SERVICE;
#endif /* BROKEN_LIKE_POSIX */
    pservice = &gaih_service;
  } else
    pservice = NULL;

  if (pai)
    end = &p;
  else
    end = NULL;

  anyok = 0;
  for ( ; g->gaih; ++g) {
    if ((req->ai_family == g->family) || !req->ai_family) {
      j++;
      if (!((pg && (pg->gaih == g->gaih)))) {
	pg = g;
	i = g->gaih(name, pservice, req, end, vlog);
	if (i == 0)
	  anyok = 1;

if (vlog)
  fprintf(vlog,"getaddrinfo(): g->family=%d g->gaih(name='%s', service='%s') returns: %d\n", g->family, name, service ? service : "<NULL>", i);

	if (i != 0 && !anyok) {
	  if (!req->ai_family && (i & GAIH_OKIFUNSPEC))
	    continue;
	  goto gaih_err;
	}
	if (end)
          while (*end) end = &((*end)->ai_next);
      }
    }
  }

  if (!j)
    return EAI_FAMILY;

  if (p) {
    *pai = p;
    return 0;
  }

  if (!pai && !i)
    return 0;

gaih_err:
  if (p)
    freeaddrinfo(p);

  if (i)
    return -(i & GAIH_EAI);

  return EAI_NONAME;
}

int
getaddrinfo(name, service, req, pai)
const char		*name;
const char		*service;
const struct addrinfo	*req;
struct addrinfo		**pai;
{
  return _getaddrinfo_(name, service, req, pai, NULL);
}

void
freeaddrinfo(ai)
struct addrinfo *ai;
{
  struct addrinfo *p;

  while (ai != NULL) {
    p = ai;
    ai = ai->ai_next;
    free((void *)p);
  }
}


syntax highlighted by Code2HTML, v. 0.9.1