master
 1#define _GNU_SOURCE
 2
 3#include <sys/socket.h>
 4#include <netdb.h>
 5#include <string.h>
 6#include <netinet/in.h>
 7#include <errno.h>
 8#include <inttypes.h>
 9
10int gethostbyaddr_r(const void *a, socklen_t l, int af,
11	struct hostent *h, char *buf, size_t buflen,
12	struct hostent **res, int *err)
13{
14	union {
15		struct sockaddr_in sin;
16		struct sockaddr_in6 sin6;
17	} sa = { .sin.sin_family = af };
18	socklen_t sl = af==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin;
19	int i;
20
21	*res = 0;
22
23	/* Load address argument into sockaddr structure */
24	if (af==AF_INET6 && l==16) memcpy(&sa.sin6.sin6_addr, a, 16);
25	else if (af==AF_INET && l==4) memcpy(&sa.sin.sin_addr, a, 4);
26	else {
27		*err = NO_RECOVERY;
28		return EINVAL;
29	}
30
31	/* Align buffer and check for space for pointers and ip address */
32	i = (uintptr_t)buf & sizeof(char *)-1;
33	if (!i) i = sizeof(char *);
34	if (buflen <= 5*sizeof(char *)-i + l) return ERANGE;
35	buf += sizeof(char *)-i;
36	buflen -= 5*sizeof(char *)-i + l;
37
38	h->h_addr_list = (void *)buf;
39	buf += 2*sizeof(char *);
40	h->h_aliases = (void *)buf;
41	buf += 2*sizeof(char *);
42
43	h->h_addr_list[0] = buf;
44	memcpy(h->h_addr_list[0], a, l);
45	buf += l;
46	h->h_addr_list[1] = 0;
47	h->h_aliases[0] = buf;
48	h->h_aliases[1] = 0;
49
50	switch (getnameinfo((void *)&sa, sl, buf, buflen, 0, 0, 0)) {
51	case EAI_AGAIN:
52		*err = TRY_AGAIN;
53		return EAGAIN;
54	case EAI_OVERFLOW:
55		return ERANGE;
56	default:
57	case EAI_FAIL:
58		*err = NO_RECOVERY;
59		return EBADMSG;
60	case EAI_SYSTEM:
61		*err = NO_RECOVERY;
62		return errno;
63	case 0:
64		break;
65	}
66
67	h->h_addrtype = af;
68	h->h_length = l;
69	h->h_name = h->h_aliases[0];
70	*res = h;
71	return 0;
72}