diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/network/getaddrinfo.c | 322 | ||||
| -rw-r--r-- | src/network/lookup.h | 26 | ||||
| -rw-r--r-- | src/network/lookup_name.c | 168 | ||||
| -rw-r--r-- | src/network/lookup_serv.c | 72 | 
4 files changed, 360 insertions, 228 deletions
diff --git a/src/network/getaddrinfo.c b/src/network/getaddrinfo.c index 5d45be74..70b6cfda 100644 --- a/src/network/getaddrinfo.c +++ b/src/network/getaddrinfo.c @@ -1,249 +1,115 @@  #include <stdlib.h> -#include <stdio.h> -#include <netdb.h> -#include <netinet/in.h>  #include <sys/socket.h> -#include <unistd.h> +#include <netinet/in.h> +#include <netdb.h>  #include <string.h> -#include <ctype.h> -#include "__dns.h" -#include "stdio_impl.h" - -static int is_valid(const char *host) -{ -	const unsigned char *s; -	if (strlen(host)-1 > 254 || mbstowcs(0, host, 0) > 255) return 0; -	for (s=(void *)host; *s>=0x80 || *s=='.' || *s=='-' || isalnum(*s); s++); -	return !*s; -} - -#if 0 -static int have_af(int family) -{ -	struct sockaddr_in6 sin6 = { .sin6_family = family }; -	socklen_t sl = family == AF_INET -		? sizeof(struct sockaddr_in) -		: sizeof(struct sockaddr_in6); -	int sock = socket(family, SOCK_STREAM, 0); -	int have = !bind(sock, (void *)&sin6, sl); -	close(sock); -	return have; -} -#endif - -union sa { -	struct sockaddr_in sin; -	struct sockaddr_in6 sin6; -}; - -struct aibuf { -	struct addrinfo ai; -	union sa sa; -}; - -/* Extra slots needed for storing canonical name */ -#define EXTRA ((256+sizeof(struct aibuf)-1)/sizeof(struct aibuf)) +#include "lookup.h"  int getaddrinfo(const char *restrict host, const char *restrict serv, const struct addrinfo *restrict hint, struct addrinfo **restrict res)  { -	int flags = hint ? hint->ai_flags : 0; -	int family = hint ? hint->ai_family : AF_UNSPEC; -	int type = hint ? hint->ai_socktype : 0; -	int proto = hint ? hint->ai_protocol : 0; -	unsigned long port = 0; -	struct aibuf *buf; -	union sa sa = {{0}}; -	unsigned char reply[1024]; -	int i, j; -	char line[512]; -	FILE *f, _f; -	unsigned char _buf[1024]; -	char *z; -	int result; -	int cnt; - -	if (family != AF_INET && family != AF_INET6 && family != AF_UNSPEC) -		return EAI_FAMILY; - -	if (host && strlen(host)>255) return EAI_NONAME; -	if (serv && strlen(serv)>32) return EAI_SERVICE; - -	if (type && !proto) -		proto = type==SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP; -	if (!type && proto) -		type = proto==IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM; - -	if (serv) { -		if (!*serv) return EAI_SERVICE; -		port = strtoul(serv, &z, 10); -		if (*z) { -			size_t servlen = strlen(serv); -			char *end = line; - -			if (flags & AI_NUMERICSERV) return EAI_SERVICE; +	struct service ports[MAXSERVS]; +	struct address addrs[MAXADDRS]; +	char canon[256], *outcanon; +	int nservs, naddrs, nais, canon_len, i, j, k; +	int family = AF_UNSPEC, flags = 0, proto = 0; +	struct aibuf { +		struct addrinfo ai; +		union sa { +			struct sockaddr_in sin; +			struct sockaddr_in6 sin6; +		} sa; +	} *out; + +	if (hint) { +		family = hint->ai_family; +		flags = hint->ai_flags; +		proto = hint->ai_protocol; + +		const int mask = AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST | +			AI_V4MAPPED | AI_ALL | AI_ADDRCONFIG | AI_NUMERICSERV; +		if ((flags & mask) != flags) +			return EAI_BADFLAGS; + +		switch (family) { +		case AF_INET: +		case AF_INET6: +		case AF_UNSPEC: +			break; +		default: +			return EAI_FAMILY; +		} -			f = __fopen_rb_ca("/etc/services", &_f, _buf, sizeof _buf); -			if (!f) return EAI_SERVICE; -			while (fgets(line, sizeof line, f)) { -				if (strncmp(line, serv, servlen) || !isspace(line[servlen])) -					continue; -				port = strtoul(line+servlen, &end, 10); -				if (strncmp(end, proto==IPPROTO_UDP ? "/udp" : "/tcp", 4)) -					continue; +		switch (hint->ai_socktype) { +		case SOCK_STREAM: +			switch (proto) { +			case 0: +				proto = IPPROTO_TCP; +			case IPPROTO_TCP:  				break; +			default: +				return EAI_SERVICE;  			} -			__fclose_ca(f); -			if (feof(f)) return EAI_SERVICE; -		} -		if (port > 65535) return EAI_SERVICE; -		port = htons(port); -	} - -	if (!host) { -		if (family == AF_UNSPEC) { -			cnt = 2; family = AF_INET; -		} else { -			cnt = 1; -		} -		buf = calloc(sizeof *buf, cnt); -		if (!buf) return EAI_MEMORY; -		for (i=0; i<cnt; i++) { -			if (i) family = AF_INET6; -			buf[i].ai.ai_protocol = proto; -			buf[i].ai.ai_socktype = type; -			buf[i].ai.ai_addr = (void *)&buf[i].sa; -			buf[i].ai.ai_addrlen = family==AF_INET6 -				? sizeof sa.sin6 : sizeof sa.sin; -			buf[i].ai.ai_family = family; -			buf[i].sa.sin.sin_family = family; -			buf[i].sa.sin.sin_port = port; -			if (i+1<cnt) buf[i].ai.ai_next = &buf[i+1].ai; -			if (!(flags & AI_PASSIVE)) { -				if (family == AF_INET) { -					0[(uint8_t*)&buf[i].sa.sin.sin_addr.s_addr]=127; -					3[(uint8_t*)&buf[i].sa.sin.sin_addr.s_addr]=1; -				} else buf[i].sa.sin6.sin6_addr.s6_addr[15] = 1; +			break; +		case SOCK_DGRAM: +			switch (proto) { +			case 0: +				proto = IPPROTO_UDP; +			case IPPROTO_UDP: +				break; +			default: +				return EAI_SERVICE;  			} +		case 0: +			break; +		default: +			return EAI_SOCKTYPE;  		} -		*res = &buf->ai; -		return 0; -	} - -	if (!*host) return EAI_NONAME; - -	/* Try as a numeric address */ -	if (__ipparse(&sa, family, host) >= 0) { -		buf = calloc(sizeof *buf, 1+EXTRA); -		if (!buf) return EAI_MEMORY; -		family = sa.sin.sin_family; -		buf->ai.ai_protocol = proto; -		buf->ai.ai_socktype = type; -		buf->ai.ai_addr = (void *)&buf->sa; -		buf->ai.ai_addrlen = family==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin; -		buf->ai.ai_family = family; -		buf->ai.ai_canonname = (char *)host; -		buf->sa = sa; -		buf->sa.sin.sin_port = port; -		*res = &buf->ai; -		return 0;  	} -	if (flags & AI_NUMERICHOST) return EAI_NONAME; +	nservs = __lookup_serv(ports, serv, proto, flags); +	if (nservs < 0) return nservs; -	f = __fopen_rb_ca("/etc/hosts", &_f, _buf, sizeof _buf); -	if (f) while (fgets(line, sizeof line, f)) { -		char *p; -		size_t l = strlen(host); +	naddrs = __lookup_name(addrs, canon, host, family, flags); +	if (naddrs < 0) return naddrs; -		if ((p=strchr(line, '#'))) *p++='\n', *p=0; -		for(p=line+1; (p=strstr(p, host)) && -			(!isspace(p[-1]) || !isspace(p[l])); p++); -		if (!p) continue; -		__fclose_ca(f); - -		/* Isolate IP address to parse */ -		for (p=line; *p && !isspace(*p); p++); -		*p++ = 0; -		if (__ipparse(&sa, family, line) < 0) return EAI_NONAME; - -		/* Allocate and fill result buffer */ -		buf = calloc(sizeof *buf, 1+EXTRA); -		if (!buf) return EAI_MEMORY; -		family = sa.sin.sin_family; -		buf->ai.ai_protocol = proto; -		buf->ai.ai_socktype = type; -		buf->ai.ai_addr = (void *)&buf->sa; -		buf->ai.ai_addrlen = family==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin; -		buf->ai.ai_family = family; -		buf->sa = sa; -		buf->sa.sin.sin_port = port; - -		/* Extract first name as canonical name */ -		for (; *p && isspace(*p); p++); -		buf->ai.ai_canonname = (void *)(buf+1); -		snprintf(buf->ai.ai_canonname, 256, "%s", p); -		for (p=buf->ai.ai_canonname; *p && !isspace(*p); p++); -		*p = 0; -		if (!is_valid(buf->ai.ai_canonname)) -			buf->ai.ai_canonname = 0; - -		*res = &buf->ai; -		return 0; -	} -	if (f) __fclose_ca(f); +	nais = nservs * naddrs; +	canon_len = strlen(canon); +	out = calloc(1, nais * sizeof(*out) + canon_len + 1); +	if (!out) return EAI_MEMORY; -#if 0 -	f = __fopen_rb_ca("/etc/resolv.conf", &_f, _buf, sizeof _buf); -	if (f) while (fgets(line, sizeof line, f)) { -		if (!isspace(line[10]) || (strncmp(line, "search", 6) -			&& strncmp(line, "domain", 6))) continue; +	if (canon_len) { +		outcanon = (void *)&out[nais]; +		memcpy(outcanon, canon, canon_len+1); +	} else { +		outcanon = 0;  	} -	if (f) __fclose_ca(f); -#endif -	/* Perform one or more DNS queries for host */ -	memset(reply, 0, sizeof reply); -	result = __dns_query(reply, host, family, 0); -	if (result < 0) return result; - -	cnt = __dns_count_addrs(reply, result); -	if (cnt <= 0) return EAI_NONAME; - -	buf = calloc(sizeof *buf, cnt+EXTRA); -	if (!buf) return EAI_MEMORY; - -	i = 0; -	if (family != AF_INET6) { -		j = __dns_get_rr(&buf[i].sa.sin.sin_addr, sizeof *buf, 4, cnt-i, reply, RR_A, 0); -		while (j--) buf[i++].sa.sin.sin_family = AF_INET; -	} -	if (family != AF_INET) { -		j = __dns_get_rr(&buf[i].sa.sin6.sin6_addr, sizeof *buf, 16, cnt-i, reply, RR_AAAA, 0); -		while (j--) buf[i++].sa.sin.sin_family = AF_INET6; -	} -	if (result>1) { -		j = __dns_get_rr(&buf[i].sa.sin.sin_addr, sizeof *buf, 4, cnt-i, reply+512, RR_A, 0); -		while (j--) buf[i++].sa.sin.sin_family = AF_INET; -		j = __dns_get_rr(&buf[i].sa.sin6.sin6_addr, sizeof *buf, 16, cnt-i, reply+512, RR_AAAA, 0); -		while (j--) buf[i++].sa.sin.sin_family = AF_INET6; -	} - -	if (__dns_get_rr((void *)&buf[cnt], 0, 256, 1, reply, RR_CNAME, 1) <= 0) -		strcpy((void *)&buf[cnt], host); - -	for (i=0; i<cnt; i++) { -		buf[i].ai.ai_protocol = proto; -		buf[i].ai.ai_socktype = type; -		buf[i].ai.ai_addr = (void *)&buf[i].sa; -		buf[i].ai.ai_addrlen = buf[i].sa.sin.sin_family==AF_INET6 -			? sizeof sa.sin6 : sizeof sa.sin; -		buf[i].ai.ai_family = buf[i].sa.sin.sin_family; -		buf[i].sa.sin.sin_port = port; -		buf[i].ai.ai_next = &buf[i+1].ai; -		buf[i].ai.ai_canonname = (void *)&buf[cnt]; +	for (k=i=0; i<naddrs; i++) for (j=0; j<nservs; j++, k++) { +		out[k].ai = (struct addrinfo){ +			.ai_family = addrs[i].family, +			.ai_socktype = ports[j].proto == IPPROTO_TCP +				? SOCK_STREAM : SOCK_DGRAM, +			.ai_protocol = ports[j].proto, +			.ai_addrlen = addrs[i].family == AF_INET +				? sizeof(struct sockaddr_in) +				: sizeof(struct sockaddr_in6), +			.ai_addr = (void *)&out[k].sa, +			.ai_canonname = outcanon, +			.ai_next = &out[k+1].ai }; +		switch (addrs[i].family) { +		case AF_INET: +			out[k].sa.sin.sin_family = AF_INET; +			out[k].sa.sin.sin_port = htons(ports[j].port); +			memcpy(&out[k].sa.sin.sin_addr, &addrs[i].addr, 4); +			break; +		case AF_INET6: +			out[k].sa.sin6.sin6_family = AF_INET6; +			out[k].sa.sin6.sin6_port = htons(ports[j].port); +			memcpy(&out[k].sa.sin6.sin6_addr, &addrs[i].addr, 16); +			break;			 +		}  	} -	buf[cnt-1].ai.ai_next = 0; -	*res = &buf->ai; - +	out[nais-1].ai.ai_next = 0; +	*res = &out->ai;  	return 0;  } diff --git a/src/network/lookup.h b/src/network/lookup.h new file mode 100644 index 00000000..82c969ec --- /dev/null +++ b/src/network/lookup.h @@ -0,0 +1,26 @@ +#ifndef LOOKUP_H +#define LOOKUP_H + +#include <stdint.h> + +struct address { +	int family; +	unsigned scopeid; +	uint8_t addr[16]; +}; + +struct service { +	uint16_t port; +	char proto; +}; + +/* The limit of 48 results is a non-sharp bound on the number of addresses + * that can fit in one 512-byte DNS packet full of v4 results and a second + * packet full of v6 results. Due to headers, the actual limit is lower. */ +#define MAXADDRS 48 +#define MAXSERVS 2 + +int __lookup_serv(struct service buf[static MAXSERVS], const char *name, int proto, int flags); +int __lookup_name(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family, int flags); + +#endif diff --git a/src/network/lookup_name.c b/src/network/lookup_name.c new file mode 100644 index 00000000..b1f1ffd0 --- /dev/null +++ b/src/network/lookup_name.c @@ -0,0 +1,168 @@ +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include "lookup.h" +#include "stdio_impl.h" +#include "syscall.h" +#include "__dns.h" + +static int is_valid_hostname(const char *host) +{ +	const unsigned char *s; +	if (strnlen(host, 255)-1 > 254 || mbstowcs(0, host, 0) > 255) return 0; +	for (s=(void *)host; *s>=0x80 || *s=='.' || *s=='-' || isalnum(*s); s++); +	return !*s; +} + +static int name_from_null(struct address buf[static 2], const char *name, int family, int flags) +{ +	int cnt = 0; +	if (name) return 0; +	if (flags & AI_PASSIVE) { +		if (family != AF_INET6) +			buf[cnt++] = (struct address){ .family = AF_INET }; +		if (family != AF_INET) +			buf[cnt++] = (struct address){ .family = AF_INET6 }; +	} else { +		if (family != AF_INET6) +			buf[cnt++] = (struct address){ .family = AF_INET, .addr = { 127,0,0,1 } }; +		if (family != AF_INET) +			buf[cnt++] = (struct address){ .family = AF_INET6, .addr = { [15] = 1 } }; +	} +	return cnt; +} + +static int name_from_numeric(struct address buf[static 1], const char *name, int family) +{ +	struct in_addr a4; +	struct in6_addr a6; +	if (family != AF_INET6 && inet_aton(name, &a4)>0) { +		memcpy(&buf[0].addr, &a4, sizeof a4); +		buf[0].family = AF_INET; +		return 1; +	} +	if (family != AF_INET && inet_pton(AF_INET6, name, &a6)>0) { +		memcpy(&buf[0].addr, &a6, sizeof a6); +		buf[0].family = AF_INET6; +		return 1; +	} +	return 0; +} + +static int name_from_hosts(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family) +{ +	char line[512]; +	size_t l = strlen(name); +	int cnt = 0; +	unsigned char _buf[1032]; +	FILE _f, *f = __fopen_rb_ca("/etc/hosts", &_f, _buf, sizeof _buf); +	if (!f) return 0; +	while (fgets(line, sizeof line, f) && cnt < MAXADDRS) { +		char *p, *z; + +		if ((p=strchr(line, '#'))) *p++='\n', *p=0; +		for(p=line+1; (p=strstr(p, name)) && +			(!isspace(p[-1]) || !isspace(p[l])); p++); +		if (!p) continue; + +		/* Isolate IP address to parse */ +		for (p=line; *p && !isspace(*p); p++); +		*p++ = 0; +		if (name_from_numeric(buf+cnt, line, family)) +			cnt++; + +		/* Extract first name as canonical name */ +		for (; *p && isspace(*p); p++); +		for (z=p; *z && !isspace(*z); z++); +		*z = 0; +		if (is_valid_hostname(p)) memcpy(canon, p, z-p+1); +	} +	__fclose_ca(f); +	return cnt; +} + +static int name_from_dns(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family) +{ +	unsigned char reply[1024] = { 0 }, *p = reply; +	char tmp[256]; +	int i, cnt = 0; + +	/* Perform one or more DNS queries for host */ +	int result = __dns_query(reply, name, family, 0); +	if (result < 0) return result; + +	for (i=0; i<result; i++) { +		if (family != AF_INET6) { +			int j = __dns_get_rr(&buf[cnt].addr, sizeof *buf, 4, MAXADDRS-cnt, p, RR_A, 0); +			while (j--) buf[cnt++].family = AF_INET; +		} +		if (family != AF_INET) { +			int j = __dns_get_rr(&buf[cnt].addr, sizeof *buf, 16, MAXADDRS-cnt, p, RR_AAAA, 0); +			while (j--) buf[cnt++].family = AF_INET6; +		} +		p += 512; +	} +	__dns_get_rr(tmp, 0, 256, 1, reply, RR_CNAME, 1); +	if (is_valid_hostname(tmp)) strcpy(canon, tmp); +	return cnt; +} + +int __lookup_name(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family, int flags) +{ +	int cnt = 0, i, j; + +	*canon = 0; +	if (name) { +		size_t l; +		if ((l = strnlen(name, 255))-1 > 254) +			return EAI_NONAME; +		memcpy(canon, name, l+1); +	} + +	/* Procedurally, a request for v6 addresses with the v4-mapped +	 * flag set is like a request for unspecified family, followed +	 * by filtering of the results. */ +	if (flags & AI_V4MAPPED) { +		if (family == AF_INET6) family = AF_UNSPEC; +		else flags -= AI_V4MAPPED; +	} + +	/* Try each backend until there's at least one result. */ +	cnt = name_from_null(buf, name, family, flags); +	if (cnt<=0) cnt = name_from_numeric(buf, name, family); +	if (cnt<=0 && !(flags & AI_NUMERICHOST)) { +		cnt = name_from_hosts(buf, canon, name, family); +		if (cnt<=0) cnt = name_from_dns(buf, canon, name, family); +	} +	if (cnt<=0) return cnt ? cnt : EAI_NONAME; + +	/* Filter/transform results for v4-mapped lookup, if requested. */ +	if (flags & AI_V4MAPPED) { +		if (!(flags & AI_ALL)) { +			/* If any v6 results exist, remove v4 results. */ +			for (i=0; i<cnt && buf[i].family != AF_INET6; i++); +			if (i<cnt) { +				for (j=0; i<cnt; i++) { +					if (buf[i].family == AF_INET6) +						buf[j++] = buf[i]; +				} +				cnt = i = j; +			} +		} +		/* Translate any remaining v4 results to v6 */ +		for (i=0; i<cnt; i++) { +			if (buf[i].family != AF_INET) continue; +			memcpy(buf[i].addr+12, buf[i].addr, 4); +			memcpy(buf[i].addr, "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12); +			buf[i].scopeid = 0; +			buf[i].family = AF_INET6; +		} +	} + +	return cnt; +} diff --git a/src/network/lookup_serv.c b/src/network/lookup_serv.c new file mode 100644 index 00000000..bf4cba09 --- /dev/null +++ b/src/network/lookup_serv.c @@ -0,0 +1,72 @@ +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <ctype.h> +#include <string.h> +#include <fcntl.h> +#include "lookup.h" +#include "stdio_impl.h" + +int __lookup_serv(struct service buf[static MAXSERVS], const char *name, int proto, int flags) +{ +	char line[128]; +	int cnt = 0; +	char *p, *z = ""; +	unsigned long port = 0; + +	if (name) { +		if (!*name) return EAI_SERVICE; +		port = strtoul(name, &z, 10); +	} +	if (!*z) { +		if (port > 65535) return EAI_SERVICE; +		if (proto != IPPROTO_UDP) { +			buf[cnt].port = port; +			buf[cnt++].proto = IPPROTO_TCP; +		} +		if (proto != IPPROTO_TCP) { +			buf[cnt].port = port; +			buf[cnt++].proto = IPPROTO_UDP; +		} +		return cnt; +	} + +	if (flags & AI_NUMERICSERV) return EAI_SERVICE; + +	size_t l = strlen(name); + +	unsigned char _buf[1032]; +	FILE _f, *f = __fopen_rb_ca("/etc/services", &_f, _buf, sizeof _buf); +	if (!f) return EAI_SERVICE; + +	while (fgets(line, sizeof line, f) && cnt < MAXSERVS) { +		if ((p=strchr(line, '#'))) *p++='\n', *p=0; + +		/* Find service name */ +		for(p=line; (p=strstr(p, name)); p++) { +			if (p>line && !isspace(p[-1])) continue; +			if (p[l] && !isspace(p[l])) continue; +			break; +		} +		if (!p) continue; + +		/* Skip past canonical name at beginning of line */ +		for (p=line; *p && !isspace(*p); p++); +		if (!p) continue; + +		port = strtoul(p, &z, 10); +		if (port > 65535 || z==p) continue; +		if (!strncmp(z, "/udp", 4)) { +			if (proto == IPPROTO_TCP) continue; +			buf[cnt].port = port; +			buf[cnt++].proto = IPPROTO_UDP; +		} +		if (!strncmp(z, "/tcp", 4)) { +			if (proto == IPPROTO_UDP) continue; +			buf[cnt].port = port; +			buf[cnt++].proto = IPPROTO_TCP; +		} +	} +	__fclose_ca(f); +	return cnt > 0 ? cnt : EAI_SERVICE; +}  | 
