Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20140530044105.GU507@brightrain.aerifal.cx>
Date: Fri, 30 May 2014 00:41:06 -0400
From: Rich Felker <dalias@...c.org>
To: musl@...ts.openwall.com
Subject: Resolver overhaul started

I've started working on the resolver overhaul with a design based on
what I described in the thread "Resolver overhaul concepts". The code
that's written so far is the outer "getaddrinfo" layer, which uses new
internal interfaces for name and service resolving and transforms the
output into the linked list format (which, as before, is just an array
with each member pointing to the next) covering the direct product of
the address results and the service results. This consolidates all of
the "struct addrinfo" production in one place, rather than having it
duplicated/unrolled all over the place in different cases for
passive/localhost, numeric addresses, hosts file, and dns, and makes
it so requests that accept either tcp or udp results work correctly.

The internal interfaces, which are not yet implemented (well,
__lookup_name partially is, but it's missing the interesting parts)
are responsible for taking a string and other parameters and filling a
caller-provided, fixed-size list of results. Applying filters based on
the requested protocol/address-family/flags/etc. is their
responsibility, as will be (if/when we add it) sorting the results per
RFC 3484. These results are intended to be easily usable by legacy
functions like gethostbyname without going through the getaddrinfo
API, so that the legacy functions can be implemented without
malloc/free (in practice I'll still make them use malloc, to avoid bss
bloat in libc.so, but they won't need free, so __simple_malloc can be
used when static linking).

Various things may still need minor tweaking; for example, the format
used for returning addresses may need to change somewhat to
accommodate ipv6 scope ids (which are not supported at all in the old
code, but should be). But the overall factoring and code flow seem to
be how I want them. See below for the current draft. Comments welcome.

Rich




struct address {
	int family;
	uint8_t addr[16];
};

struct service {
	uint16_t port;
	char protocol;
};

int __lookup_serv(struct service buf[static 2], const char *name, int proto);
int __lookup_name(union address *buf[static 32], char canon[static 256], const char *name, int family, int flags);

int getaddrinfo(const char *restrict host, const char *restrict serv, const struct addrinfo *restrict hint, struct addrinfo **restrict res)
{
	struct service ports[2];
	struct address addrs[32];
	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;
		}

		switch (hint->ai_socktype) {
		case SOCK_STREAM:
			switch (proto) {
			case 0:
				proto = IPPROTO_TCP;
			case IPPROTO_TCP:
				break;
			default:
				return EAI_SERVICE;
			}
			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;
		}
	}

	nservs = __lookup_serv(ports, serv, proto, flags);
	if (nservs < 0) return nservs;

	naddrs = __lookup_name(addrs, canon, host, family, flags);
	if (nadds < 0) return naddrs;

	nais = nservs * naddrs;
	canon_len = strlen(canon);
	out = calloc(1, nais * sizeof(*out) + canon_len + 1);
	if (!out) return EAI_MEMORY;

	if (canon_len) {
		outcanon = (void *)out[nais];
		memcpy(outcanon, canon, canon_len+1);
	} else {
		outcanon = 0;
	}

	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(sturct 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;			
		}
	}
	out[nais-1].ai_next = 0;
	*res = out;
	return 0;
}

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.