gethostbyaddr_r.c raw

   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  
  10  int 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