blob: 10f8d6b88282d03cbfe45011a67ff997a836e9ad [file] [log] [blame]
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
static int roundup(int x, int m) {
return (((x + m - 1)) & ~(m - 1));
}
int gethostbyname2_r(const char* name, int af, struct hostent* h, char* buf, size_t buflen,
struct hostent** res, int* err) {
*err = 0;
*res = NULL;
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = af;
hints.ai_socktype = SOCK_STREAM;
struct addrinfo *addrinfo;
int eai = getaddrinfo(name, NULL, &hints, &addrinfo);
if (eai != 0) {
switch (eai) {
case EAI_NONAME:
*err = HOST_NOT_FOUND;
break;
case EAI_AGAIN:
*err = TRY_AGAIN;
break;
default:
*err = NO_RECOVERY;
break;
}
return 0;
}
h->h_addrtype = af;
h->h_length = (af == AF_INET) ? 4 : 16;
int namelen = strlen(name);
int n_addr = 0;
for (struct addrinfo *ap = addrinfo; ap != NULL; ap = ap->ai_next) {
++n_addr;
}
size_t need = roundup(namelen + 1, sizeof(char*)); // h_name
need += sizeof(char*); // h_aliases
need += sizeof(char*) * (n_addr + 1); // h_addr_list
need += h->h_length * n_addr; // addrs that h_addr_list points to
if (need > buflen) {
freeaddrinfo(addrinfo);
*err = -1;
return ERANGE;
}
memcpy(buf, name, namelen + 1);
h->h_name = buf;
buf += roundup(namelen + 1, sizeof(char*));
h->h_aliases = (char**)buf;
h->h_aliases[0] = NULL;
buf += sizeof(char*);
h->h_addr_list = (char**)buf;
buf += sizeof(char*) * (n_addr + 1);
n_addr = 0;
for (struct addrinfo *ap = addrinfo; ap != NULL; ap = ap->ai_next) {
if (af == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)ap->ai_addr;
memcpy(buf, &sin->sin_addr, h->h_length);
} else if (af == AF_INET6) {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)ap->ai_addr;
memcpy(buf, &sin6->sin6_addr, h->h_length);
}
h->h_addr_list[n_addr++] = buf;
buf += h->h_length;
}
h->h_addr_list[n_addr] = NULL;
freeaddrinfo(addrinfo);
*res = h;
return 0;
}