Logo Search packages:      
Sourcecode: zmailer version File versions  Download package

zgetaddrinfo.c

/*
 * 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>
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif

#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>


#ifndef NS_INT16SZ
# define NS_INT16SZ 2
#endif
#ifndef NS_INT32SZ
# define NS_INT32SZ 4
#endif

#ifndef NS_GET16
#define NS_GET16(s, cp) do { \
        register u_char *t_cp = (u_char *)(cp); \
        (s) = ((u_int16_t)t_cp[0] << 8) \
            | ((u_int16_t)t_cp[1]) \
            ; \
        (cp) += NS_INT16SZ; \
} while (0)

#define NS_GET32(l, cp) do { \
        register u_char *t_cp = (u_char *)(cp); \
        (l) = ((u_int32_t)t_cp[0] << 24) \
            | ((u_int32_t)t_cp[1] << 16) \
            | ((u_int32_t)t_cp[2] << 8) \
            | ((u_int32_t)t_cp[3]) \
            ; \
        (cp) += NS_INT32SZ; \
} while (0)
#endif

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);
    }
    NS_GET16(qt, p);
    NS_GET16(qc, p);
#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 {
      NS_GET16(j, p);      /* type */
      NS_GET16(rclass, p); /* 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  */
      NS_GET16(i, p); /* 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);
  }
}

Generated by  Doxygen 1.6.0   Back to index