getnameinfo.c raw

   1  #include <netdb.h>
   2  #include <limits.h>
   3  #include <string.h>
   4  #include <stdio.h>
   5  #include <stdlib.h>
   6  #include <sys/socket.h>
   7  #include <netinet/in.h>
   8  #include <arpa/inet.h>
   9  #include <net/if.h>
  10  #include <ctype.h>
  11  #include <resolv.h>
  12  #include "lookup.h"
  13  #include "stdio_impl.h"
  14  
  15  #define PTR_MAX (64 + sizeof ".in-addr.arpa")
  16  #define RR_PTR 12
  17  
  18  static char *itoa(char *p, unsigned x) {
  19  	p += 3*sizeof(int);
  20  	*--p = 0;
  21  	do {
  22  		*--p = '0' + x % 10;
  23  		x /= 10;
  24  	} while (x);
  25  	return p;
  26  }
  27  
  28  static void mkptr4(char *s, const unsigned char *ip)
  29  {
  30  	sprintf(s, "%d.%d.%d.%d.in-addr.arpa",
  31  		ip[3], ip[2], ip[1], ip[0]);
  32  }
  33  
  34  static void mkptr6(char *s, const unsigned char *ip)
  35  {
  36  	static const char xdigits[] = "0123456789abcdef";
  37  	int i;
  38  	for (i=15; i>=0; i--) {
  39  		*s++ = xdigits[ip[i]&15]; *s++ = '.';
  40  		*s++ = xdigits[ip[i]>>4]; *s++ = '.';
  41  	}
  42  	strcpy(s, "ip6.arpa");
  43  }
  44  
  45  static void reverse_hosts(char *buf, const unsigned char *a, unsigned scopeid, int family)
  46  {
  47  	char line[512], *p, *z;
  48  	unsigned char _buf[1032], atmp[16];
  49  	struct address iplit;
  50  	FILE _f, *f = __fopen_rb_ca("/etc/hosts", &_f, _buf, sizeof _buf);
  51  	if (!f) return;
  52  	if (family == AF_INET) {
  53  		memcpy(atmp+12, a, 4);
  54  		memcpy(atmp, "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12);
  55  		a = atmp;
  56  	}
  57  	while (fgets(line, sizeof line, f)) {
  58  		if ((p=strchr(line, '#'))) *p++='\n', *p=0;
  59  
  60  		for (p=line; *p && !isspace(*p); p++);
  61  		*p++ = 0;
  62  		if (__lookup_ipliteral(&iplit, line, AF_UNSPEC)<=0)
  63  			continue;
  64  
  65  		if (iplit.family == AF_INET) {
  66  			memcpy(iplit.addr+12, iplit.addr, 4);
  67  			memcpy(iplit.addr, "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12);
  68  			iplit.scopeid = 0;
  69  		}
  70  
  71  		if (memcmp(a, iplit.addr, 16) || iplit.scopeid != scopeid)
  72  			continue;
  73  			
  74  		for (; *p && isspace(*p); p++);
  75  		for (z=p; *z && !isspace(*z); z++);
  76  		*z = 0;
  77  		if (z-p < 256) {
  78  			memcpy(buf, p, z-p+1);
  79  			break;
  80  		}
  81  	}
  82  	__fclose_ca(f);
  83  }
  84  
  85  static void reverse_services(char *buf, int port, int dgram)
  86  {
  87  	unsigned long svport;
  88  	char line[128], *p, *z;
  89  	unsigned char _buf[1032];
  90  	FILE _f, *f = __fopen_rb_ca("/etc/services", &_f, _buf, sizeof _buf);
  91  	if (!f) return;
  92  	while (fgets(line, sizeof line, f)) {
  93  		if ((p=strchr(line, '#'))) *p++='\n', *p=0;
  94  
  95  		for (p=line; *p && !isspace(*p); p++);
  96  		if (!*p) continue;
  97  		*p++ = 0;
  98  		svport = strtoul(p, &z, 10);
  99  
 100  		if (svport != port || z==p) continue;
 101  		if (dgram && strncmp(z, "/udp", 4)) continue;
 102  		if (!dgram && strncmp(z, "/tcp", 4)) continue;
 103  		if (p-line > 32) continue;
 104  
 105  		memcpy(buf, line, p-line);
 106  		break;
 107  	}
 108  	__fclose_ca(f);
 109  }
 110  
 111  static int dns_parse_callback(void *c, int rr, const void *data, int len, const void *packet)
 112  {
 113  	if (rr != RR_PTR) return 0;
 114  	if (__dn_expand(packet, (const unsigned char *)packet + 512,
 115  	    data, c, 256) <= 0)
 116  		*(char *)c = 0;
 117  	return 0;
 118  	
 119  }
 120  
 121  int getnameinfo(const struct sockaddr *restrict sa, socklen_t sl,
 122  	char *restrict node, socklen_t nodelen,
 123  	char *restrict serv, socklen_t servlen,
 124  	int flags)
 125  {
 126  	char ptr[PTR_MAX];
 127  	char buf[256], num[3*sizeof(int)+1];
 128  	int af = sa->sa_family;
 129  	unsigned char *a;
 130  	unsigned scopeid;
 131  
 132  	switch (af) {
 133  	case AF_INET:
 134  		a = (void *)&((struct sockaddr_in *)sa)->sin_addr;
 135  		if (sl < sizeof(struct sockaddr_in)) return EAI_FAMILY;
 136  		mkptr4(ptr, a);
 137  		scopeid = 0;
 138  		break;
 139  	case AF_INET6:
 140  		a = (void *)&((struct sockaddr_in6 *)sa)->sin6_addr;
 141  		if (sl < sizeof(struct sockaddr_in6)) return EAI_FAMILY;
 142  		if (memcmp(a, "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12))
 143  			mkptr6(ptr, a);
 144  		else
 145  			mkptr4(ptr, a+12);
 146  		scopeid = ((struct sockaddr_in6 *)sa)->sin6_scope_id;
 147  		break;
 148  	default:
 149  		return EAI_FAMILY;
 150  	}
 151  
 152  	if (node && nodelen) {
 153  		buf[0] = 0;
 154  		if (!(flags & NI_NUMERICHOST)) {
 155  			reverse_hosts(buf, a, scopeid, af);
 156  		}
 157  		if (!*buf && !(flags & NI_NUMERICHOST)) {
 158  			unsigned char query[18+PTR_MAX], reply[512];
 159  			int qlen = __res_mkquery(0, ptr, 1, RR_PTR,
 160  				0, 0, 0, query, sizeof query);
 161  			query[3] = 0; /* don't need AD flag */
 162  			int rlen = __res_send(query, qlen, reply, sizeof reply);
 163  			buf[0] = 0;
 164  			if (rlen > 0)
 165  				__dns_parse(reply, rlen, dns_parse_callback, buf);
 166  		}
 167  		if (!*buf) {
 168  			if (flags & NI_NAMEREQD) return EAI_NONAME;
 169  			inet_ntop(af, a, buf, sizeof buf);
 170  			if (scopeid) {
 171  				char *p = 0, tmp[IF_NAMESIZE+1];
 172  				if (!(flags & NI_NUMERICSCOPE) &&
 173  				    (IN6_IS_ADDR_LINKLOCAL(a) ||
 174  				     IN6_IS_ADDR_MC_LINKLOCAL(a)))
 175  					p = if_indextoname(scopeid, tmp+1);
 176  				if (!p)
 177  					p = itoa(num, scopeid);
 178  				*--p = '%';
 179  				strcat(buf, p);
 180  			}
 181  		}
 182  		if (strlen(buf) >= nodelen) return EAI_OVERFLOW;
 183  		strcpy(node, buf);
 184  	}
 185  
 186  	if (serv && servlen) {
 187  		char *p = buf;
 188  		int port = ntohs(((struct sockaddr_in *)sa)->sin_port);
 189  		buf[0] = 0;
 190  		if (!(flags & NI_NUMERICSERV))
 191  			reverse_services(buf, port, flags & NI_DGRAM);
 192  		if (!*p)
 193  			p = itoa(num, port);
 194  		if (strlen(p) >= servlen)
 195  			return EAI_OVERFLOW;
 196  		strcpy(serv, p);
 197  	}
 198  
 199  	return 0;
 200  }
 201