#ifndef FAI_DEFINED
#define FAI_DEFINED
#include "port-sockets.h"
#include "socket-utils.h"
#include "k5-platform.h"
#include "k5-thread.h"
#include <stdio.h>
#include <errno.h>
#ifdef S_SPLINT_S
extern int
getaddrinfo ( const char *,
const char *,
const struct addrinfo *,
struct addrinfo **)
;
extern void
freeaddrinfo ( struct addrinfo *)
;
extern int
getnameinfo (const struct sockaddr *addr, socklen_t addrsz,
char *h, socklen_t hsz,
char *s, socklen_t ssz,
int flags)
;
extern char *gai_strerror (int code) ;
#endif
#if defined (__APPLE__) && defined (__MACH__)
#define FAI_CACHE
#endif
#if (defined (__linux__) && defined(HAVE_GETADDRINFO)) || defined (_AIX)
# define WRAP_GETADDRINFO
#endif
#if defined (__linux__) && defined(HAVE_GETADDRINFO)
# define COPY_FIRST_CANONNAME
#endif
#ifdef _AIX
# define NUMERIC_SERVICE_BROKEN
# define COPY_FIRST_CANONNAME
#endif
#ifdef COPY_FIRST_CANONNAME
# include <string.h>
#endif
#ifdef NUMERIC_SERVICE_BROKEN
# include <ctype.h>
# include <stdlib.h>
#endif
#ifdef _WIN32
#define HAVE_GETADDRINFO 1
#define HAVE_GETNAMEINFO 1
#endif
#if !defined(HAVE_GETHOSTBYNAME_R) || defined(THREADSAFE_GETHOSTBYNAME)
#define GET_HOST_BY_NAME(NAME, HP, ERR) \
{ (HP) = gethostbyname (NAME); (ERR) = h_errno; }
#define GET_HOST_BY_ADDR(ADDR, ADDRLEN, FAMILY, HP, ERR) \
{ (HP) = gethostbyaddr ((ADDR), (ADDRLEN), (FAMILY)); (ERR) = h_errno; }
#else
#ifdef _AIX
#define GET_HOST_BY_NAME(NAME, HP, ERR) \
{ \
struct hostent my_h_ent; \
struct hostent_data my_h_ent_data; \
(HP) = (gethostbyname_r((NAME), &my_h_ent, &my_h_ent_data) \
? 0 \
: &my_h_ent); \
(ERR) = h_errno; \
}
#else
#ifdef GETHOSTBYNAME_R_RETURNS_INT
#define GET_HOST_BY_NAME(NAME, HP, ERR) \
{ \
struct hostent my_h_ent, *my_hp; \
int my_h_err; \
char my_h_buf[8192]; \
(HP) = (gethostbyname_r((NAME), &my_h_ent, \
my_h_buf, sizeof (my_h_buf), &my_hp, \
&my_h_err) \
? 0 \
: &my_h_ent); \
(ERR) = my_h_err; \
}
#define GET_HOST_BY_ADDR(ADDR, ADDRLEN, FAMILY, HP, ERR) \
{ \
struct hostent my_h_ent, *my_hp; \
int my_h_err; \
char my_h_buf[8192]; \
(HP) = (gethostbyaddr_r((ADDR), (ADDRLEN), (FAMILY), &my_h_ent, \
my_h_buf, sizeof (my_h_buf), &my_hp, \
&my_h_err) \
? 0 \
: &my_h_ent); \
(ERR) = my_h_err; \
}
#else
#define GET_HOST_BY_NAME(NAME, HP, ERR) \
{ \
struct hostent my_h_ent; \
int my_h_err; \
char my_h_buf[8192]; \
(HP) = gethostbyname_r((NAME), &my_h_ent, \
my_h_buf, sizeof (my_h_buf), &my_h_err); \
(ERR) = my_h_err; \
}
#define GET_HOST_BY_ADDR(ADDR, ADDRLEN, FAMILY, HP, ERR) \
{ \
struct hostent my_h_ent; \
int my_h_err; \
char my_h_buf[8192]; \
(HP) = gethostbyaddr_r((ADDR), (ADDRLEN), (FAMILY), &my_h_ent, \
my_h_buf, sizeof (my_h_buf), &my_h_err); \
(ERR) = my_h_err; \
}
#endif
#endif
#endif
#ifndef HAVE_GETSERVBYNAME_R
#define GET_SERV_BY_NAME(NAME, PROTO, SP, ERR) \
((SP) = getservbyname (NAME, PROTO), (ERR) = (SP) ? 0 : -1)
#define GET_SERV_BY_PORT(PORT, PROTO, SP, ERR) \
((SP) = getservbyport (PORT, PROTO), (ERR) = (SP) ? 0 : -1)
#else
#ifdef GETSERVBYNAME_R_RETURNS_INT
#define GET_SERV_BY_NAME(NAME, PROTO, SP, ERR) \
{ \
struct servent my_s_ent, *my_sp; \
int my_s_err; \
char my_s_buf[8192]; \
(SP) = (getservbyname_r((NAME), (PROTO), &my_s_ent, \
my_s_buf, sizeof (my_s_buf), &my_sp, \
&my_s_err) \
? 0 \
: &my_s_ent); \
(ERR) = my_s_err; \
}
#define GET_SERV_BY_PORT(PORT, PROTO, SP, ERR) \
{ \
struct servent my_s_ent, *my_sp; \
int my_s_err; \
char my_s_buf[8192]; \
(SP) = (getservbyport_r((PORT), (PROTO), &my_s_ent, \
my_s_buf, sizeof (my_s_buf), &my_sp, \
&my_s_err) \
? 0 \
: &my_s_ent); \
(ERR) = my_s_err; \
}
#else
#define GET_SERV_BY_NAME(NAME, PROTO, SP, ERR) \
{ \
struct servent my_s_ent; \
char my_s_buf[8192]; \
(SP) = getservbyname_r((NAME), (PROTO), &my_s_ent, \
my_s_buf, sizeof (my_s_buf)); \
(ERR) = (SP) == NULL; \
}
#define GET_SERV_BY_PORT(PORT, PROTO, SP, ERR) \
{ \
struct servent my_s_ent, *my_sp; \
char my_s_buf[8192]; \
my_sp = getservbyport_r((PORT), (PROTO), &my_s_ent, \
my_s_buf, sizeof (my_s_buf)); \
(SP) = my_sp; \
(ERR) = my_sp == 0; \
(ERR) = (ERR); \
}
#endif
#endif
#if defined(WRAP_GETADDRINFO) || defined(FAI_CACHE)
static inline int
system_getaddrinfo (const char *name, const char *serv,
const struct addrinfo *hint,
struct addrinfo **res)
{
return getaddrinfo(name, serv, hint, res);
}
static inline void
system_freeaddrinfo (struct addrinfo *ai)
{
freeaddrinfo(ai);
}
static inline int
system_getnameinfo (const struct sockaddr *sa, socklen_t salen,
char *host, size_t hostlen, char *serv, size_t servlen,
int flags)
{
return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
}
#endif
#if !defined (HAVE_GETADDRINFO) || defined(WRAP_GETADDRINFO) || defined(FAI_CACHE)
#undef getaddrinfo
#define getaddrinfo my_fake_getaddrinfo
#undef freeaddrinfo
#define freeaddrinfo my_fake_freeaddrinfo
#endif
#if !defined (HAVE_GETADDRINFO)
#undef gai_strerror
#define gai_strerror my_fake_gai_strerror
#undef addrinfo
#define addrinfo my_fake_addrinfo
struct addrinfo {
int ai_family;
int ai_socktype;
int ai_protocol;
int ai_flags;
size_t ai_addrlen;
char *ai_canonname;
struct sockaddr *ai_addr;
struct addrinfo *ai_next;
};
#undef AI_PASSIVE
#define AI_PASSIVE 0x01
#undef AI_CANONNAME
#define AI_CANONNAME 0x02
#undef AI_NUMERICHOST
#define AI_NUMERICHOST 0x04
#undef AI_V4MAPPED
#define AI_V4MAPPED 0
#undef AI_ADDRCONFIG
#define AI_ADDRCONFIG 0
#undef AI_ALL
#define AI_ALL 0
#undef AI_DEFAULT
#define AI_DEFAULT (AI_V4MAPPED|AI_ADDRCONFIG)
#ifndef NI_MAXHOST
#define NI_MAXHOST 1025
#endif
#ifndef NI_MAXSERV
#define NI_MAXSERV 32
#endif
#undef NI_NUMERICHOST
#define NI_NUMERICHOST 0x01
#undef NI_NUMERICSERV
#define NI_NUMERICSERV 0x02
#undef NI_NAMEREQD
#define NI_NAMEREQD 0x04
#undef NI_DGRAM
#define NI_DGRAM 0x08
#undef NI_NOFQDN
#define NI_NOFQDN 0x10
#undef EAI_ADDRFAMILY
#define EAI_ADDRFAMILY 1
#undef EAI_AGAIN
#define EAI_AGAIN 2
#undef EAI_BADFLAGS
#define EAI_BADFLAGS 3
#undef EAI_FAIL
#define EAI_FAIL 4
#undef EAI_FAMILY
#define EAI_FAMILY 5
#undef EAI_MEMORY
#define EAI_MEMORY 6
#undef EAI_NODATA
#define EAI_NODATA 7
#undef EAI_NONAME
#define EAI_NONAME 8
#undef EAI_SERVICE
#define EAI_SERVICE 9
#undef EAI_SOCKTYPE
#define EAI_SOCKTYPE 10
#undef EAI_SYSTEM
#define EAI_SYSTEM 11
#endif
#if (!defined (HAVE_GETADDRINFO) || defined (WRAP_GETADDRINFO)) && defined(DEBUG_ADDRINFO)
static const char *protoname (int p, char *buf) {
#define X(N) if (p == IPPROTO_ ## N) return #N
X(TCP);
X(UDP);
X(ICMP);
X(IPV6);
#ifdef IPPROTO_GRE
X(GRE);
#endif
X(NONE);
X(RAW);
#ifdef IPPROTO_COMP
X(COMP);
#endif
sprintf(buf, " %-2d", p);
return buf;
}
static const char *socktypename (int t, char *buf) {
switch (t) {
case SOCK_DGRAM: return "DGRAM";
case SOCK_STREAM: return "STREAM";
case SOCK_RAW: return "RAW";
case SOCK_RDM: return "RDM";
case SOCK_SEQPACKET: return "SEQPACKET";
}
sprintf(buf, " %-2d", t);
return buf;
}
static const char *familyname (int f, char *buf) {
switch (f) {
default:
sprintf(buf, "AF %d", f);
return buf;
case AF_INET: return "AF_INET";
case AF_INET6: return "AF_INET6";
#ifdef AF_UNIX
case AF_UNIX: return "AF_UNIX";
#endif
}
}
static void debug_dump_getaddrinfo_args (const char *name, const char *serv,
const struct addrinfo *hint)
{
const char *sep;
fprintf(stderr,
"getaddrinfo(hostname %s, service %s,\n"
" hints { ",
name ? name : "(null)", serv ? serv : "(null)");
if (hint) {
char buf[30];
sep = "";
#define Z(FLAG) if (hint->ai_flags & AI_##FLAG) fprintf(stderr, "%s%s", sep, #FLAG), sep = "|"
Z(CANONNAME);
Z(PASSIVE);
#ifdef AI_NUMERICHOST
Z(NUMERICHOST);
#endif
if (sep[0] == 0)
fprintf(stderr, "no-flags");
if (hint->ai_family)
fprintf(stderr, " %s", familyname(hint->ai_family, buf));
if (hint->ai_socktype)
fprintf(stderr, " SOCK_%s", socktypename(hint->ai_socktype, buf));
if (hint->ai_protocol)
fprintf(stderr, " IPPROTO_%s", protoname(hint->ai_protocol, buf));
} else
fprintf(stderr, "(null)");
fprintf(stderr, " }):\n");
}
static void debug_dump_error (int err)
{
fprintf(stderr, "error %d: %s\n", err, gai_strerror(err));
}
static void debug_dump_addrinfos (const struct addrinfo *ai)
{
int count = 0;
fprintf(stderr, "addrinfos returned:\n");
while (ai) {
fprintf(stderr, "%p...", ai);
fprintf(stderr, " socktype=%s", socktypename(ai->ai_socktype));
fprintf(stderr, " ai_family=%s", familyname(ai->ai_family));
if (ai->ai_family != ai->ai_addr->sa_family)
fprintf(stderr, " sa_family=%s",
familyname(ai->ai_addr->sa_family));
fprintf(stderr, "\n");
ai = ai->ai_next;
count++;
}
fprintf(stderr, "end addrinfos returned (%d)\n");
}
#endif
#if !defined (HAVE_GETADDRINFO) || defined (WRAP_GETADDRINFO)
static
int getaddrinfo (const char *name, const char *serv,
const struct addrinfo *hint, struct addrinfo **result);
static
void freeaddrinfo (struct addrinfo *ai);
#endif
#if !defined (HAVE_GETADDRINFO)
#define HAVE_FAKE_GETADDRINFO
#define HAVE_GETADDRINFO
#define NEED_FAKE_GETNAMEINFO
#undef HAVE_GETNAMEINFO
#define HAVE_GETNAMEINFO 1
#undef getnameinfo
#define getnameinfo my_fake_getnameinfo
static
char *gai_strerror (int code);
#endif
#if !defined (HAVE_GETADDRINFO)
static
int getnameinfo (const struct sockaddr *addr, socklen_t len,
char *host, socklen_t hostlen,
char *service, socklen_t servicelen,
int flags);
#endif
#ifndef AI_NUMERICHOST
# define AI_NUMERICHOST 0
#endif
#ifndef AI_ADDRCONFIG
# define AI_ADDRCONFIG 0
#endif
#ifndef AI_V4MAPPED
# define AI_V4MAPPED 0
#endif
#ifndef AI_ALL
# define AI_ALL 0
#endif
#ifndef AI_DEFAULT
# define AI_DEFAULT (AI_ADDRCONFIG|AI_V4MAPPED)
#endif
#if defined(HAVE_FAKE_GETADDRINFO) || defined(FAI_CACHE)
#define NEED_FAKE_GETADDRINFO
#endif
#if defined(NEED_FAKE_GETADDRINFO) || defined(WRAP_GETADDRINFO)
#include <stdlib.h>
#endif
struct face {
struct in_addr *addrs4;
struct in6_addr *addrs6;
unsigned int naddrs4, naddrs6;
time_t expiration;
char *canonname, *name;
struct face *next;
};
struct fac {
k5_mutex_t lock;
struct face *data;
};
extern struct fac krb5int_fac;
#ifdef NEED_FAKE_GETADDRINFO
#include <string.h>
static inline int translate_h_errno (int h);
static inline int fai_add_entry (struct addrinfo **result, void *addr,
int port, const struct addrinfo *template)
{
struct addrinfo *n = malloc (sizeof (struct addrinfo));
if (n == 0)
return EAI_MEMORY;
if (template->ai_family != AF_INET
#ifdef KRB5_USE_INET6
&& template->ai_family != AF_INET6
#endif
)
return EAI_FAMILY;
*n = *template;
if (template->ai_family == AF_INET) {
struct sockaddr_in *sin4;
sin4 = malloc (sizeof (struct sockaddr_in));
if (sin4 == 0)
return EAI_MEMORY;
n->ai_addr = (struct sockaddr *) sin4;
sin4->sin_family = AF_INET;
sin4->sin_addr = *(struct in_addr *)addr;
sin4->sin_port = port;
#ifdef HAVE_SA_LEN
sin4->sin_len = sizeof (struct sockaddr_in);
#endif
}
#ifdef KRB5_USE_INET6
if (template->ai_family == AF_INET6) {
struct sockaddr_in6 *sin6;
sin6 = malloc (sizeof (struct sockaddr_in6));
if (sin6 == 0)
return EAI_MEMORY;
n->ai_addr = (struct sockaddr *) sin6;
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = *(struct in6_addr *)addr;
sin6->sin6_port = port;
#ifdef HAVE_SA_LEN
sin6->sin6_len = sizeof (struct sockaddr_in6);
#endif
}
#endif
n->ai_next = *result;
*result = n;
return 0;
}
#ifdef FAI_CACHE
#define CACHE_ENTRY_LIFETIME 15
static void plant_face (const char *name, struct face *entry)
{
entry->name = strdup(name);
if (entry->name == NULL)
return;
k5_mutex_assert_locked(&krb5int_fac.lock);
entry->next = krb5int_fac.data;
entry->expiration = time(0) + CACHE_ENTRY_LIFETIME;
krb5int_fac.data = entry;
#ifdef DEBUG_ADDRINFO
printf("added cache entry '%s' at %p: %d ipv4, %d ipv6; expire %d\n",
name, entry, entry->naddrs4, entry->naddrs6, entry->expiration);
#endif
}
static int find_face (const char *name, struct face **entry)
{
struct face *fp, **fpp;
time_t now = time(0);
#ifdef DEBUG_ADDRINFO
printf("scanning cache at %d for '%s'...\n", now, name);
#endif
k5_mutex_assert_locked(&krb5int_fac.lock);
for (fpp = &krb5int_fac.data; *fpp; ) {
fp = *fpp;
#ifdef DEBUG_ADDRINFO
printf(" checking expiration time of @%p: %d\n",
fp, fp->expiration);
#endif
if (fp->expiration < now) {
#ifdef DEBUG_ADDRINFO
printf("\texpiring cache entry\n");
#endif
free(fp->name);
free(fp->canonname);
free(fp->addrs4);
free(fp->addrs6);
*fpp = fp->next;
free(fp);
} else
fpp = &(*fpp)->next;
}
for (fp = krb5int_fac.data; fp; fp = fp->next) {
#ifdef DEBUG_ADDRINFO
printf(" comparing entry @%p\n", fp);
#endif
if (!strcasecmp(fp->name, name)) {
#ifdef DEBUG_ADDRINFO
printf("\tMATCH!\n");
#endif
*entry = fp;
return 1;
}
}
return 0;
}
#endif
extern int krb5int_lock_fac(void), krb5int_unlock_fac(void);
static inline int fai_add_hosts_by_name (const char *name,
struct addrinfo *template,
int portnum, int flags,
struct addrinfo **result)
{
#ifdef FAI_CACHE
struct face *ce;
int i, r, err;
err = krb5int_lock_fac();
if (err) {
errno = err;
return EAI_SYSTEM;
}
if (!find_face(name, &ce)) {
struct addrinfo myhints = { 0 }, *ai, *ai2;
int i4, i6, aierr;
#ifdef DEBUG_ADDRINFO
printf("looking up new data for '%s'...\n", name);
#endif
myhints.ai_socktype = SOCK_STREAM;
myhints.ai_flags = AI_CANONNAME;
aierr = system_getaddrinfo(name, NULL, &myhints, &ai);
if (aierr) {
krb5int_unlock_fac();
return aierr;
}
ce = malloc(sizeof(struct face));
memset(ce, 0, sizeof(*ce));
ce->expiration = time(0) + 30;
for (ai2 = ai; ai2; ai2 = ai2->ai_next) {
#ifdef DEBUG_ADDRINFO
printf(" found an address in family %d...\n", ai2->ai_family);
#endif
switch (ai2->ai_family) {
case AF_INET:
ce->naddrs4++;
break;
case AF_INET6:
ce->naddrs6++;
break;
default:
break;
}
}
ce->addrs4 = calloc(ce->naddrs4, sizeof(*ce->addrs4));
if (ce->addrs4 == NULL && ce->naddrs4 != 0) {
krb5int_unlock_fac();
system_freeaddrinfo(ai);
return EAI_MEMORY;
}
ce->addrs6 = calloc(ce->naddrs6, sizeof(*ce->addrs6));
if (ce->addrs6 == NULL && ce->naddrs6 != 0) {
krb5int_unlock_fac();
free(ce->addrs4);
system_freeaddrinfo(ai);
return EAI_MEMORY;
}
for (ai2 = ai, i4 = i6 = 0; ai2; ai2 = ai2->ai_next) {
switch (ai2->ai_family) {
case AF_INET:
ce->addrs4[i4++] = ((struct sockaddr_in *)ai2->ai_addr)->sin_addr;
break;
case AF_INET6:
ce->addrs6[i6++] = ((struct sockaddr_in6 *)ai2->ai_addr)->sin6_addr;
break;
default:
break;
}
}
ce->canonname = ai->ai_canonname ? strdup(ai->ai_canonname) : 0;
system_freeaddrinfo(ai);
plant_face(name, ce);
}
template->ai_family = AF_INET6;
template->ai_addrlen = sizeof(struct sockaddr_in6);
for (i = 0; i < ce->naddrs6; i++) {
r = fai_add_entry (result, &ce->addrs6[i], portnum, template);
if (r) {
krb5int_unlock_fac();
return r;
}
}
template->ai_family = AF_INET;
template->ai_addrlen = sizeof(struct sockaddr_in);
for (i = 0; i < ce->naddrs4; i++) {
r = fai_add_entry (result, &ce->addrs4[i], portnum, template);
if (r) {
krb5int_unlock_fac();
return r;
}
}
if (*result && (flags & AI_CANONNAME))
(*result)->ai_canonname = (ce->canonname
? strdup(ce->canonname)
: NULL);
krb5int_unlock_fac();
return 0;
#else
struct hostent *hp;
int i, r;
int herr;
GET_HOST_BY_NAME (name, hp, herr);
if (hp == 0)
return translate_h_errno (herr);
for (i = 0; hp->h_addr_list[i]; i++) {
r = fai_add_entry (result, hp->h_addr_list[i], portnum, template);
if (r)
return r;
}
if (*result && (flags & AI_CANONNAME))
(*result)->ai_canonname = strdup (hp->h_name);
return 0;
#endif
}
static inline void
fake_freeaddrinfo (struct addrinfo *ai)
{
struct addrinfo *next;
while (ai) {
next = ai->ai_next;
if (ai->ai_canonname)
free (ai->ai_canonname);
if (ai->ai_addr)
free (ai->ai_addr);
free (ai);
ai = next;
}
}
static inline int
fake_getaddrinfo (const char *name, const char *serv,
const struct addrinfo *hint, struct addrinfo **result)
{
struct addrinfo *res = 0;
int ret;
int port = 0, socktype;
int flags;
struct addrinfo template;
#ifdef DEBUG_ADDRINFO
debug_dump_getaddrinfo_args(name, serv, hint);
#endif
if (hint != 0) {
if (hint->ai_family != 0 && hint->ai_family != AF_INET)
return EAI_NODATA;
socktype = hint->ai_socktype;
flags = hint->ai_flags;
} else {
socktype = 0;
flags = 0;
}
if (serv) {
size_t numlen = strspn (serv, "0123456789");
if (serv[numlen] == '\0') {
unsigned long p = strtoul (serv, 0, 10);
if (p == 0 || p > 65535)
return EAI_NONAME;
port = htons (p);
} else {
struct servent *sp;
int try_dgram_too = 0, s_err;
if (socktype == 0) {
try_dgram_too = 1;
socktype = SOCK_STREAM;
}
try_service_lookup:
GET_SERV_BY_NAME(serv, socktype == SOCK_STREAM ? "tcp" : "udp",
sp, s_err);
if (sp == 0) {
if (try_dgram_too) {
socktype = SOCK_DGRAM;
goto try_service_lookup;
}
return EAI_SERVICE;
}
port = sp->s_port;
}
}
if (name == 0) {
name = (flags & AI_PASSIVE) ? "0.0.0.0" : "127.0.0.1";
flags |= AI_NUMERICHOST;
}
template.ai_family = AF_INET;
template.ai_addrlen = sizeof (struct sockaddr_in);
template.ai_socktype = socktype;
template.ai_protocol = 0;
template.ai_flags = 0;
template.ai_canonname = 0;
template.ai_next = 0;
template.ai_addr = 0;
if (flags & AI_NUMERICHOST) {
struct in_addr addr4;
#if 0
ret = inet_aton (name, &addr4);
if (ret)
return EAI_NONAME;
#else
addr4.s_addr = inet_addr (name);
if (addr4.s_addr == 0xffffffff || addr4.s_addr == -1)
return EAI_NONAME;
#endif
ret = fai_add_entry (&res, &addr4, port, &template);
} else {
ret = fai_add_hosts_by_name (name, &template, port, flags,
&res);
}
if (ret && ret != NO_ADDRESS) {
fake_freeaddrinfo (res);
return ret;
}
if (res == 0)
return NO_ADDRESS;
*result = res;
return 0;
}
#ifdef NEED_FAKE_GETNAMEINFO
static inline int
fake_getnameinfo (const struct sockaddr *sa, socklen_t len,
char *host, socklen_t hostlen,
char *service, socklen_t servicelen,
int flags)
{
struct hostent *hp;
const struct sockaddr_in *sinp;
struct servent *sp;
size_t hlen, slen;
if (sa->sa_family != AF_INET) {
return EAI_FAMILY;
}
sinp = (const struct sockaddr_in *) sa;
hlen = hostlen;
if (hostlen < 0 || hlen != hostlen) {
errno = EINVAL;
return EAI_SYSTEM;
}
slen = servicelen;
if (servicelen < 0 || slen != servicelen) {
errno = EINVAL;
return EAI_SYSTEM;
}
if (host) {
if (flags & NI_NUMERICHOST) {
#if (defined(__GNUC__) && defined(__mips__)) || 1
const unsigned char *uc;
char tmpbuf[20];
numeric_host:
uc = (const unsigned char *) &sinp->sin_addr;
sprintf(tmpbuf, "%d.%d.%d.%d", uc[0], uc[1], uc[2], uc[3]);
strncpy(host, tmpbuf, hlen);
#else
char *p;
numeric_host:
p = inet_ntoa (sinp->sin_addr);
strncpy (host, p, hlen);
#endif
} else {
int herr;
GET_HOST_BY_ADDR((const char *) &sinp->sin_addr,
sizeof (struct in_addr),
sa->sa_family, hp, herr);
if (hp == 0) {
if (herr == NO_ADDRESS && !(flags & NI_NAMEREQD))
goto numeric_host;
return translate_h_errno (herr);
}
strncpy (host, hp->h_name, hlen);
}
host[hostlen-1] = 0;
}
if (service) {
if (flags & NI_NUMERICSERV) {
char numbuf[10];
int port;
numeric_service:
port = ntohs (sinp->sin_port);
if (port < 0 || port > 65535)
return EAI_FAIL;
sprintf (numbuf, "%d", port);
strncpy (service, numbuf, slen);
} else {
int serr;
GET_SERV_BY_PORT(sinp->sin_port,
(flags & NI_DGRAM) ? "udp" : "tcp",
sp, serr);
if (sp == 0)
goto numeric_service;
strncpy (service, sp->s_name, slen);
}
service[servicelen-1] = 0;
}
return 0;
}
#endif
#if defined(HAVE_FAKE_GETADDRINFO) || defined(NEED_FAKE_GETNAMEINFO)
static inline
char *gai_strerror (int code)
{
switch (code) {
case EAI_ADDRFAMILY: return "address family for nodename not supported";
case EAI_AGAIN: return "temporary failure in name resolution";
case EAI_BADFLAGS: return "bad flags to getaddrinfo/getnameinfo";
case EAI_FAIL: return "non-recoverable failure in name resolution";
case EAI_FAMILY: return "ai_family not supported";
case EAI_MEMORY: return "out of memory";
case EAI_NODATA: return "no address associated with hostname";
case EAI_NONAME: return "name does not exist";
case EAI_SERVICE: return "service name not supported for specified socket type";
case EAI_SOCKTYPE: return "ai_socktype not supported";
case EAI_SYSTEM: return strerror (errno);
default: return "bogus getaddrinfo error?";
}
}
#endif
static inline int translate_h_errno (int h)
{
switch (h) {
case 0:
return 0;
#ifdef NETDB_INTERNAL
case NETDB_INTERNAL:
if (errno == ENOMEM)
return EAI_MEMORY;
return EAI_SYSTEM;
#endif
case HOST_NOT_FOUND:
return EAI_NONAME;
case TRY_AGAIN:
return EAI_AGAIN;
case NO_RECOVERY:
return EAI_FAIL;
case NO_DATA:
#if NO_DATA != NO_ADDRESS
case NO_ADDRESS:
#endif
return EAI_NODATA;
default:
return EAI_SYSTEM;
}
}
#if defined(HAVE_FAKE_GETADDRINFO) || defined(FAI_CACHE)
static inline
int getaddrinfo (const char *name, const char *serv,
const struct addrinfo *hint, struct addrinfo **result)
{
return fake_getaddrinfo(name, serv, hint, result);
}
static inline
void freeaddrinfo (struct addrinfo *ai)
{
fake_freeaddrinfo(ai);
}
#ifdef NEED_FAKE_GETNAMEINFO
static inline
int getnameinfo (const struct sockaddr *sa, socklen_t len,
char *host, socklen_t hostlen,
char *service, socklen_t servicelen,
int flags)
{
return fake_getnameinfo(sa, len, host, hostlen, service, servicelen,
flags);
}
#endif
#endif
#endif
#ifdef WRAP_GETADDRINFO
static inline
int
getaddrinfo (const char *name, const char *serv, const struct addrinfo *hint,
struct addrinfo **result)
{
int aierr;
#if defined(_AIX) || defined(COPY_FIRST_CANONNAME)
struct addrinfo *ai;
#endif
#ifdef NUMERIC_SERVICE_BROKEN
int service_is_numeric = 0;
int service_port = 0;
int socket_type = 0;
#endif
#ifdef DEBUG_ADDRINFO
debug_dump_getaddrinfo_args(name, serv, hint);
#endif
#ifdef NUMERIC_SERVICE_BROKEN
if (serv && serv[0] && isdigit(serv[0])) {
unsigned long lport;
char *end;
lport = strtoul(serv, &end, 10);
if (!*end) {
if (lport > 65535)
return EAI_SOCKTYPE;
service_is_numeric = 1;
service_port = htons(lport);
serv = "discard";
if (hint)
socket_type = hint->ai_socktype;
}
}
#endif
aierr = system_getaddrinfo (name, serv, hint, result);
if (aierr || *result == 0) {
#ifdef DEBUG_ADDRINFO
debug_dump_error(aierr);
#endif
return aierr;
}
#ifdef COPY_FIRST_CANONNAME
ai = *result;
if (ai->ai_canonname) {
struct hostent *hp;
const char *name2 = 0;
int i, herr;
GET_HOST_BY_NAME (name, hp, herr);
if (hp == 0) {
if (ai->ai_canonname && strchr(ai->ai_canonname, ':'))
ai->ai_canonname = 0;
name2 = ai->ai_canonname ? ai->ai_canonname : name;
} else {
for (i = 0; hp->h_aliases[i]; i++) {
if (strchr(hp->h_aliases[i], '.') != 0) {
name2 = hp->h_aliases[i];
break;
}
}
if (hp->h_aliases[i] == 0)
name2 = hp->h_name;
}
ai->ai_canonname = strdup(name2);
if (name2 != 0 && ai->ai_canonname == 0) {
system_freeaddrinfo(ai);
*result = 0;
#ifdef DEBUG_ADDRINFO
debug_dump_error(EAI_MEMORY);
#endif
return EAI_MEMORY;
}
while ((ai = ai->ai_next) != NULL)
ai->ai_canonname = 0;
}
#endif
#ifdef NUMERIC_SERVICE_BROKEN
if (service_port != 0) {
for (ai = *result; ai; ai = ai->ai_next) {
if (socket_type != 0 && ai->ai_socktype == 0)
ai->ai_socktype = socket_type;
switch (ai->ai_family) {
case AF_INET:
((struct sockaddr_in *)ai->ai_addr)->sin_port = service_port;
break;
case AF_INET6:
((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = service_port;
break;
}
}
}
#endif
#ifdef _AIX
for (ai = *result; ai; ai = ai->ai_next) {
ai->ai_addr->sa_family = ai->ai_family;
#ifdef HAVE_SA_LEN
ai->ai_addr->sa_len = ai->ai_addrlen;
#endif
}
#endif
#ifdef DEBUG_ADDRINFO
debug_dump_addrinfos(*result);
#endif
return 0;
}
static inline
void freeaddrinfo (struct addrinfo *ai)
{
#ifdef COPY_FIRST_CANONNAME
if (ai) {
free(ai->ai_canonname);
ai->ai_canonname = 0;
system_freeaddrinfo(ai);
}
#else
system_freeaddrinfo(ai);
#endif
}
#endif
#if defined(KRB5_USE_INET6) && defined(NEED_INSIXADDR_ANY)
#undef in6addr_any
#define in6addr_any krb5int_in6addr_any
static const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
#endif
#ifdef ADDRINFO_UNDEF_INLINE
# undef inline
# undef ADDRINFO_UNDEF_INLINE
#endif
#endif