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_MEMORY:
58	case EAI_SYSTEM:
59	case EAI_FAIL:
60		*err = NO_RECOVERY;
61		return errno;
62	case 0:
63		break;
64	}
65
66	h->h_addrtype = af;
67	h->h_length = l;
68	h->h_name = h->h_aliases[0];
69	*res = h;
70	return 0;
71}
72