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