/* | |
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. | |
* All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions | |
* are met: | |
* 1. Redistributions of source code must retain the above copyright | |
* notice, this list of conditions and the following disclaimer. | |
* 2. Redistributions in binary form must reproduce the above copyright | |
* notice, this list of conditions and the following disclaimer in the | |
* documentation and/or other materials provided with the distribution. | |
* 3. Neither the name of the project nor the names of its contributors | |
* may be used to endorse or promote products derived from this software | |
* without specific prior written permission. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND | |
* GAI_ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE | |
* FOR GAI_ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
* HOWEVER CAUSED AND ON GAI_ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN GAI_ANY WAY | |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
* SUCH DAMAGE. | |
*/ | |
/* | |
* "#ifdef FAITH" part is local hack for supporting IPv4-v6 translator. | |
* | |
* Issues to be discussed: | |
* - Thread safe-ness must be checked. | |
* - Return values. There are nonstandard return values defined and used | |
* in the source code. This is because RFC2133 is silent about which error | |
* code must be returned for which situation. | |
* - PF_UNSPEC case would be handled in getipnodebyname() with the AI_ALL flag. | |
*/ | |
#if 0 | |
#include <sys/types.h> | |
#include <sys/param.h> | |
#include <sys/sysctl.h> | |
#include <sys/socket.h> | |
#include <netinet/in.h> | |
#include <arpa/inet.h> | |
#include <arpa/nameser.h> | |
#include <netdb.h> | |
#include <resolv.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <stddef.h> | |
#include <ctype.h> | |
#include <unistd.h> | |
#include "addrinfo.h" | |
#endif | |
#if defined(__KAME__) && defined(ENABLE_IPV6) | |
# define FAITH | |
#endif | |
#define SUCCESS 0 | |
#define GAI_ANY 0 | |
#define YES 1 | |
#define NO 0 | |
#ifdef FAITH | |
static int translate = NO; | |
static struct in6_addr faith_prefix = IN6ADDR_GAI_ANY_INIT; | |
#endif | |
static const char in_addrany[] = { 0, 0, 0, 0 }; | |
static const char in6_addrany[] = { | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 | |
}; | |
static const char in_loopback[] = { 127, 0, 0, 1 }; | |
static const char in6_loopback[] = { | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 | |
}; | |
struct sockinet { | |
u_char si_len; | |
u_char si_family; | |
u_short si_port; | |
}; | |
static struct gai_afd { | |
int a_af; | |
int a_addrlen; | |
int a_socklen; | |
int a_off; | |
const char *a_addrany; | |
const char *a_loopback; | |
} gai_afdl [] = { | |
#ifdef ENABLE_IPV6 | |
#define N_INET6 0 | |
{PF_INET6, sizeof(struct in6_addr), | |
sizeof(struct sockaddr_in6), | |
offsetof(struct sockaddr_in6, sin6_addr), | |
in6_addrany, in6_loopback}, | |
#define N_INET 1 | |
#else | |
#define N_INET 0 | |
#endif | |
{PF_INET, sizeof(struct in_addr), | |
sizeof(struct sockaddr_in), | |
offsetof(struct sockaddr_in, sin_addr), | |
in_addrany, in_loopback}, | |
{0, 0, 0, 0, NULL, NULL}, | |
}; | |
#ifdef ENABLE_IPV6 | |
#define PTON_MAX 16 | |
#else | |
#define PTON_MAX 4 | |
#endif | |
#ifndef IN_MULTICAST | |
#define IN_MULTICAST(i) (((i) & 0xf0000000U) == 0xe0000000U) | |
#endif | |
#ifndef IN_EXPERIMENTAL | |
#define IN_EXPERIMENTAL(i) (((i) & 0xe0000000U) == 0xe0000000U) | |
#endif | |
#ifndef IN_LOOPBACKNET | |
#define IN_LOOPBACKNET 127 | |
#endif | |
static int get_name Py_PROTO((const char *, struct gai_afd *, | |
struct addrinfo **, char *, struct addrinfo *, | |
int)); | |
static int get_addr Py_PROTO((const char *, int, struct addrinfo **, | |
struct addrinfo *, int)); | |
static int str_isnumber Py_PROTO((const char *)); | |
static char *ai_errlist[] = { | |
"success.", | |
"address family for hostname not supported.", /* EAI_ADDRFAMILY */ | |
"temporary failure in name resolution.", /* EAI_AGAIN */ | |
"invalid value for ai_flags.", /* EAI_BADFLAGS */ | |
"non-recoverable failure in name resolution.", /* EAI_FAIL */ | |
"ai_family not supported.", /* EAI_FAMILY */ | |
"memory allocation failure.", /* EAI_MEMORY */ | |
"no address associated with hostname.", /* EAI_NODATA */ | |
"hostname nor servname provided, or not known.",/* EAI_NONAME */ | |
"servname not supported for ai_socktype.", /* EAI_SERVICE */ | |
"ai_socktype not supported.", /* EAI_SOCKTYPE */ | |
"system error returned in errno.", /* EAI_SYSTEM */ | |
"invalid value for hints.", /* EAI_BADHINTS */ | |
"resolved protocol is unknown.", /* EAI_PROTOCOL */ | |
"unknown error.", /* EAI_MAX */ | |
}; | |
#define GET_CANONNAME(ai, str) \ | |
if (pai->ai_flags & AI_CANONNAME) {\ | |
if (((ai)->ai_canonname = (char *)malloc(strlen(str) + 1)) != NULL) {\ | |
strcpy((ai)->ai_canonname, (str));\ | |
} else {\ | |
error = EAI_MEMORY;\ | |
goto free;\ | |
}\ | |
} | |
#ifdef HAVE_SOCKADDR_SA_LEN | |
#define GET_AI(ai, gai_afd, addr, port) {\ | |
char *p;\ | |
if (((ai) = (struct addrinfo *)malloc(sizeof(struct addrinfo) +\ | |
((gai_afd)->a_socklen)))\ | |
== NULL) goto free;\ | |
memcpy(ai, pai, sizeof(struct addrinfo));\ | |
(ai)->ai_addr = (struct sockaddr *)((ai) + 1);\ | |
memset((ai)->ai_addr, 0, (gai_afd)->a_socklen);\ | |
(ai)->ai_addr->sa_len = (ai)->ai_addrlen = (gai_afd)->a_socklen;\ | |
(ai)->ai_addr->sa_family = (ai)->ai_family = (gai_afd)->a_af;\ | |
((struct sockinet *)(ai)->ai_addr)->si_port = port;\ | |
p = (char *)((ai)->ai_addr);\ | |
memcpy(p + (gai_afd)->a_off, (addr), (gai_afd)->a_addrlen);\ | |
} | |
#else | |
#define GET_AI(ai, gai_afd, addr, port) {\ | |
char *p;\ | |
if (((ai) = (struct addrinfo *)malloc(sizeof(struct addrinfo) +\ | |
((gai_afd)->a_socklen)))\ | |
== NULL) goto free;\ | |
memcpy(ai, pai, sizeof(struct addrinfo));\ | |
(ai)->ai_addr = (struct sockaddr *)((ai) + 1);\ | |
memset((ai)->ai_addr, 0, (gai_afd)->a_socklen);\ | |
(ai)->ai_addrlen = (gai_afd)->a_socklen;\ | |
(ai)->ai_addr->sa_family = (ai)->ai_family = (gai_afd)->a_af;\ | |
((struct sockinet *)(ai)->ai_addr)->si_port = port;\ | |
p = (char *)((ai)->ai_addr);\ | |
memcpy(p + (gai_afd)->a_off, (addr), (gai_afd)->a_addrlen);\ | |
} | |
#endif | |
#define ERR(err) { error = (err); goto bad; } | |
char * | |
gai_strerror(int ecode) | |
{ | |
if (ecode < 0 || ecode > EAI_MAX) | |
ecode = EAI_MAX; | |
return ai_errlist[ecode]; | |
} | |
void | |
freeaddrinfo(struct addrinfo *ai) | |
{ | |
struct addrinfo *next; | |
do { | |
next = ai->ai_next; | |
if (ai->ai_canonname) | |
free(ai->ai_canonname); | |
/* no need to free(ai->ai_addr) */ | |
free(ai); | |
} while ((ai = next) != NULL); | |
} | |
static int | |
str_isnumber(const char *p) | |
{ | |
unsigned char *q = (unsigned char *)p; | |
while (*q) { | |
if (! isdigit(*q)) | |
return NO; | |
q++; | |
} | |
return YES; | |
} | |
int | |
getaddrinfo(const char*hostname, const char*servname, | |
const struct addrinfo *hints, struct addrinfo **res) | |
{ | |
struct addrinfo sentinel; | |
struct addrinfo *top = NULL; | |
struct addrinfo *cur; | |
int i, error = 0; | |
char pton[PTON_MAX]; | |
struct addrinfo ai; | |
struct addrinfo *pai; | |
u_short port; | |
#ifdef FAITH | |
static int firsttime = 1; | |
if (firsttime) { | |
/* translator hack */ | |
{ | |
char *q = getenv("GAI"); | |
if (q && inet_pton(AF_INET6, q, &faith_prefix) == 1) | |
translate = YES; | |
} | |
firsttime = 0; | |
} | |
#endif | |
/* initialize file static vars */ | |
sentinel.ai_next = NULL; | |
cur = &sentinel; | |
pai = &ai; | |
pai->ai_flags = 0; | |
pai->ai_family = PF_UNSPEC; | |
pai->ai_socktype = GAI_ANY; | |
pai->ai_protocol = GAI_ANY; | |
pai->ai_addrlen = 0; | |
pai->ai_canonname = NULL; | |
pai->ai_addr = NULL; | |
pai->ai_next = NULL; | |
port = GAI_ANY; | |
if (hostname == NULL && servname == NULL) | |
return EAI_NONAME; | |
if (hints) { | |
/* error check for hints */ | |
if (hints->ai_addrlen || hints->ai_canonname || | |
hints->ai_addr || hints->ai_next) | |
ERR(EAI_BADHINTS); /* xxx */ | |
if (hints->ai_flags & ~AI_MASK) | |
ERR(EAI_BADFLAGS); | |
switch (hints->ai_family) { | |
case PF_UNSPEC: | |
case PF_INET: | |
#ifdef ENABLE_IPV6 | |
case PF_INET6: | |
#endif | |
break; | |
default: | |
ERR(EAI_FAMILY); | |
} | |
memcpy(pai, hints, sizeof(*pai)); | |
switch (pai->ai_socktype) { | |
case GAI_ANY: | |
switch (pai->ai_protocol) { | |
case GAI_ANY: | |
break; | |
case IPPROTO_UDP: | |
pai->ai_socktype = SOCK_DGRAM; | |
break; | |
case IPPROTO_TCP: | |
pai->ai_socktype = SOCK_STREAM; | |
break; | |
default: | |
pai->ai_socktype = SOCK_RAW; | |
break; | |
} | |
break; | |
case SOCK_RAW: | |
break; | |
case SOCK_DGRAM: | |
if (pai->ai_protocol != IPPROTO_UDP && | |
pai->ai_protocol != GAI_ANY) | |
ERR(EAI_BADHINTS); /*xxx*/ | |
pai->ai_protocol = IPPROTO_UDP; | |
break; | |
case SOCK_STREAM: | |
if (pai->ai_protocol != IPPROTO_TCP && | |
pai->ai_protocol != GAI_ANY) | |
ERR(EAI_BADHINTS); /*xxx*/ | |
pai->ai_protocol = IPPROTO_TCP; | |
break; | |
default: | |
ERR(EAI_SOCKTYPE); | |
/* unreachable */ | |
} | |
} | |
/* | |
* service port | |
*/ | |
if (servname) { | |
if (str_isnumber(servname)) { | |
if (pai->ai_socktype == GAI_ANY) { | |
/* caller accept *GAI_ANY* socktype */ | |
pai->ai_socktype = SOCK_DGRAM; | |
pai->ai_protocol = IPPROTO_UDP; | |
} | |
port = htons((u_short)atoi(servname)); | |
} else { | |
struct servent *sp; | |
char *proto; | |
proto = NULL; | |
switch (pai->ai_socktype) { | |
case GAI_ANY: | |
proto = NULL; | |
break; | |
case SOCK_DGRAM: | |
proto = "udp"; | |
break; | |
case SOCK_STREAM: | |
proto = "tcp"; | |
break; | |
default: | |
fprintf(stderr, "panic!\n"); | |
break; | |
} | |
if ((sp = getservbyname(servname, proto)) == NULL) | |
ERR(EAI_SERVICE); | |
port = sp->s_port; | |
if (pai->ai_socktype == GAI_ANY) { | |
if (strcmp(sp->s_proto, "udp") == 0) { | |
pai->ai_socktype = SOCK_DGRAM; | |
pai->ai_protocol = IPPROTO_UDP; | |
} else if (strcmp(sp->s_proto, "tcp") == 0) { | |
pai->ai_socktype = SOCK_STREAM; | |
pai->ai_protocol = IPPROTO_TCP; | |
} else | |
ERR(EAI_PROTOCOL); /*xxx*/ | |
} | |
} | |
} | |
/* | |
* hostname == NULL. | |
* passive socket -> anyaddr (0.0.0.0 or ::) | |
* non-passive socket -> localhost (127.0.0.1 or ::1) | |
*/ | |
if (hostname == NULL) { | |
struct gai_afd *gai_afd; | |
for (gai_afd = &gai_afdl[0]; gai_afd->a_af; gai_afd++) { | |
if (!(pai->ai_family == PF_UNSPEC | |
|| pai->ai_family == gai_afd->a_af)) { | |
continue; | |
} | |
if (pai->ai_flags & AI_PASSIVE) { | |
GET_AI(cur->ai_next, gai_afd, gai_afd->a_addrany, port); | |
/* xxx meaningless? | |
* GET_CANONNAME(cur->ai_next, "anyaddr"); | |
*/ | |
} else { | |
GET_AI(cur->ai_next, gai_afd, gai_afd->a_loopback, | |
port); | |
/* xxx meaningless? | |
* GET_CANONNAME(cur->ai_next, "localhost"); | |
*/ | |
} | |
cur = cur->ai_next; | |
} | |
top = sentinel.ai_next; | |
if (top) | |
goto good; | |
else | |
ERR(EAI_FAMILY); | |
} | |
/* hostname as numeric name */ | |
for (i = 0; gai_afdl[i].a_af; i++) { | |
if (inet_pton(gai_afdl[i].a_af, hostname, pton)) { | |
u_long v4a; | |
#ifdef ENABLE_IPV6 | |
u_char pfx; | |
#endif | |
switch (gai_afdl[i].a_af) { | |
case AF_INET: | |
v4a = ((struct in_addr *)pton)->s_addr; | |
v4a = ntohl(v4a); | |
if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) | |
pai->ai_flags &= ~AI_CANONNAME; | |
v4a >>= IN_CLASSA_NSHIFT; | |
if (v4a == 0 || v4a == IN_LOOPBACKNET) | |
pai->ai_flags &= ~AI_CANONNAME; | |
break; | |
#ifdef ENABLE_IPV6 | |
case AF_INET6: | |
pfx = ((struct in6_addr *)pton)->s6_addr8[0]; | |
if (pfx == 0 || pfx == 0xfe || pfx == 0xff) | |
pai->ai_flags &= ~AI_CANONNAME; | |
break; | |
#endif | |
} | |
if (pai->ai_family == gai_afdl[i].a_af || | |
pai->ai_family == PF_UNSPEC) { | |
if (! (pai->ai_flags & AI_CANONNAME)) { | |
GET_AI(top, &gai_afdl[i], pton, port); | |
goto good; | |
} | |
/* | |
* if AI_CANONNAME and if reverse lookup | |
* fail, return ai anyway to pacify | |
* calling application. | |
* | |
* XXX getaddrinfo() is a name->address | |
* translation function, and it looks strange | |
* that we do addr->name translation here. | |
*/ | |
get_name(pton, &gai_afdl[i], &top, pton, pai, port); | |
goto good; | |
} else | |
ERR(EAI_FAMILY); /*xxx*/ | |
} | |
} | |
if (pai->ai_flags & AI_NUMERICHOST) | |
ERR(EAI_NONAME); | |
/* hostname as alphabetical name */ | |
error = get_addr(hostname, pai->ai_family, &top, pai, port); | |
if (error == 0) { | |
if (top) { | |
good: | |
*res = top; | |
return SUCCESS; | |
} else | |
error = EAI_FAIL; | |
} | |
free: | |
if (top) | |
freeaddrinfo(top); | |
bad: | |
*res = NULL; | |
return error; | |
} | |
static int | |
get_name(addr, gai_afd, res, numaddr, pai, port0) | |
const char *addr; | |
struct gai_afd *gai_afd; | |
struct addrinfo **res; | |
char *numaddr; | |
struct addrinfo *pai; | |
int port0; | |
{ | |
u_short port = port0 & 0xffff; | |
struct hostent *hp; | |
struct addrinfo *cur; | |
int error = 0; | |
#ifdef ENABLE_IPV6 | |
int h_error; | |
#endif | |
#ifdef ENABLE_IPV6 | |
hp = getipnodebyaddr(addr, gai_afd->a_addrlen, gai_afd->a_af, &h_error); | |
#else | |
hp = gethostbyaddr(addr, gai_afd->a_addrlen, AF_INET); | |
#endif | |
if (hp && hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) { | |
GET_AI(cur, gai_afd, hp->h_addr_list[0], port); | |
GET_CANONNAME(cur, hp->h_name); | |
} else | |
GET_AI(cur, gai_afd, numaddr, port); | |
#ifdef ENABLE_IPV6 | |
if (hp) | |
freehostent(hp); | |
#endif | |
*res = cur; | |
return SUCCESS; | |
free: | |
if (cur) | |
freeaddrinfo(cur); | |
#ifdef ENABLE_IPV6 | |
if (hp) | |
freehostent(hp); | |
#endif | |
/* bad: */ | |
*res = NULL; | |
return error; | |
} | |
static int | |
get_addr(hostname, af, res, pai, port0) | |
const char *hostname; | |
int af; | |
struct addrinfo **res; | |
struct addrinfo *pai; | |
int port0; | |
{ | |
u_short port = port0 & 0xffff; | |
struct addrinfo sentinel; | |
struct hostent *hp; | |
struct addrinfo *top, *cur; | |
struct gai_afd *gai_afd; | |
int i, error = 0, h_error; | |
char *ap; | |
top = NULL; | |
sentinel.ai_next = NULL; | |
cur = &sentinel; | |
#ifdef ENABLE_IPV6 | |
if (af == AF_UNSPEC) { | |
hp = getipnodebyname(hostname, AF_INET6, | |
AI_ADDRCONFIG|AI_ALL|AI_V4MAPPED, &h_error); | |
} else | |
hp = getipnodebyname(hostname, af, AI_ADDRCONFIG, &h_error); | |
#else | |
hp = gethostbyname(hostname); | |
h_error = h_errno; | |
#endif | |
if (hp == NULL) { | |
switch (h_error) { | |
case HOST_NOT_FOUND: | |
case NO_DATA: | |
error = EAI_NODATA; | |
break; | |
case TRY_AGAIN: | |
error = EAI_AGAIN; | |
break; | |
case NO_RECOVERY: | |
default: | |
error = EAI_FAIL; | |
break; | |
} | |
goto free; | |
} | |
if ((hp->h_name == NULL) || (hp->h_name[0] == 0) || | |
(hp->h_addr_list[0] == NULL)) { | |
error = EAI_FAIL; | |
goto free; | |
} | |
for (i = 0; (ap = hp->h_addr_list[i]) != NULL; i++) { | |
switch (af) { | |
#ifdef ENABLE_IPV6 | |
case AF_INET6: | |
gai_afd = &gai_afdl[N_INET6]; | |
break; | |
#endif | |
#ifndef ENABLE_IPV6 | |
default: /* AF_UNSPEC */ | |
#endif | |
case AF_INET: | |
gai_afd = &gai_afdl[N_INET]; | |
break; | |
#ifdef ENABLE_IPV6 | |
default: /* AF_UNSPEC */ | |
if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ap)) { | |
ap += sizeof(struct in6_addr) - | |
sizeof(struct in_addr); | |
gai_afd = &gai_afdl[N_INET]; | |
} else | |
gai_afd = &gai_afdl[N_INET6]; | |
break; | |
#endif | |
} | |
#ifdef FAITH | |
if (translate && gai_afd->a_af == AF_INET) { | |
struct in6_addr *in6; | |
GET_AI(cur->ai_next, &gai_afdl[N_INET6], ap, port); | |
in6 = &((struct sockaddr_in6 *)cur->ai_next->ai_addr)->sin6_addr; | |
memcpy(&in6->s6_addr32[0], &faith_prefix, | |
sizeof(struct in6_addr) - sizeof(struct in_addr)); | |
memcpy(&in6->s6_addr32[3], ap, sizeof(struct in_addr)); | |
} else | |
#endif /* FAITH */ | |
GET_AI(cur->ai_next, gai_afd, ap, port); | |
if (cur == &sentinel) { | |
top = cur->ai_next; | |
GET_CANONNAME(top, hp->h_name); | |
} | |
cur = cur->ai_next; | |
} | |
#ifdef ENABLE_IPV6 | |
freehostent(hp); | |
#endif | |
*res = top; | |
return SUCCESS; | |
free: | |
if (top) | |
freeaddrinfo(top); | |
#ifdef ENABLE_IPV6 | |
if (hp) | |
freehostent(hp); | |
#endif | |
/* bad: */ | |
*res = NULL; | |
return error; | |
} |