| 1 | #include <stdlib.h> |
| 2 | #include <sys/socket.h> |
| 3 | #include <netinet/in.h> |
| 4 | #include <netdb.h> |
| 5 | #include <string.h> |
| 6 | #include <pthread.h> |
| 7 | #include <unistd.h> |
| 8 | #include <endian.h> |
| 9 | #include <errno.h> |
| 10 | #include "lookup.h" |
| 11 | |
| 12 | int getaddrinfo(const char *restrict host, const char *restrict serv, const struct addrinfo *restrict hint, struct addrinfo **restrict res) |
| 13 | { |
| 14 | 	struct service ports[MAXSERVS]; |
| 15 | 	struct address addrs[MAXADDRS]; |
| 16 | 	char canon[256], *outcanon; |
| 17 | 	int nservs, naddrs, nais, canon_len, i, j, k; |
| 18 | 	int family = AF_UNSPEC, flags = 0, proto = 0, socktype = 0; |
| 19 | 	int no_family = 0; |
| 20 | 	struct aibuf *out; |
| 21 | |
| 22 | 	if (!host && !serv) return EAI_NONAME; |
| 23 | |
| 24 | 	if (hint) { |
| 25 | 		family = hint->ai_family; |
| 26 | 		flags = hint->ai_flags; |
| 27 | 		proto = hint->ai_protocol; |
| 28 | 		socktype = hint->ai_socktype; |
| 29 | |
| 30 | 		const int mask = AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST | |
| 31 | 			AI_V4MAPPED | AI_ALL | AI_ADDRCONFIG | AI_NUMERICSERV; |
| 32 | 		if ((flags & mask) != flags) |
| 33 | 			return EAI_BADFLAGS; |
| 34 | |
| 35 | 		switch (family) { |
| 36 | 		case AF_INET: |
| 37 | 		case AF_INET6: |
| 38 | 		case AF_UNSPEC: |
| 39 | 			break; |
| 40 | 		default: |
| 41 | 			return EAI_FAMILY; |
| 42 | 		} |
| 43 | 	} |
| 44 | |
| 45 | 	if (flags & AI_ADDRCONFIG) { |
| 46 | 		/* Define the "an address is configured" condition for address |
| 47 | 		 * families via ability to create a socket for the family plus |
| 48 | 		 * routability of the loopback address for the family. */ |
| 49 | 		static const struct sockaddr_in lo4 = { |
| 50 | 			.sin_family = AF_INET, .sin_port = 65535, |
| 51 | 			.sin_addr.s_addr = __BYTE_ORDER == __BIG_ENDIAN |
| 52 | 				? 0x7f000001 : 0x0100007f |
| 53 | 		}; |
| 54 | 		static const struct sockaddr_in6 lo6 = { |
| 55 | 			.sin6_family = AF_INET6, .sin6_port = 65535, |
| 56 | 			.sin6_addr = IN6ADDR_LOOPBACK_INIT |
| 57 | 		}; |
| 58 | 		int tf[2] = { AF_INET, AF_INET6 }; |
| 59 | 		const void *ta[2] = { &lo4, &lo6 }; |
| 60 | 		socklen_t tl[2] = { sizeof lo4, sizeof lo6 }; |
| 61 | 		for (i=0; i<2; i++) { |
| 62 | 			if (family==tf[1-i]) continue; |
| 63 | 			int s = socket(tf[i], SOCK_CLOEXEC|SOCK_DGRAM, |
| 64 | 				IPPROTO_UDP); |
| 65 | 			if (s>=0) { |
| 66 | 				int cs; |
| 67 | 				pthread_setcancelstate( |
| 68 | 					PTHREAD_CANCEL_DISABLE, &cs); |
| 69 | 				int r = connect(s, ta[i], tl[i]); |
| 70 | 				int saved_errno = errno; |
| 71 | 				pthread_setcancelstate(cs, 0); |
| 72 | 				close(s); |
| 73 | 				if (!r) continue; |
| 74 | 				errno = saved_errno; |
| 75 | 			} |
| 76 | 			switch (errno) { |
| 77 | 			case EADDRNOTAVAIL: |
| 78 | 			case EAFNOSUPPORT: |
| 79 | 			case EHOSTUNREACH: |
| 80 | 			case ENETDOWN: |
| 81 | 			case ENETUNREACH: |
| 82 | 				break; |
| 83 | 			default: |
| 84 | 				return EAI_SYSTEM; |
| 85 | 			} |
| 86 | 			if (family == tf[i]) no_family = 1; |
| 87 | 			family = tf[1-i]; |
| 88 | 		} |
| 89 | 	} |
| 90 | |
| 91 | 	nservs = __lookup_serv(ports, serv, proto, socktype, flags); |
| 92 | 	if (nservs < 0) return nservs; |
| 93 | |
| 94 | 	naddrs = __lookup_name(addrs, canon, host, family, flags); |
| 95 | 	if (naddrs < 0) return naddrs; |
| 96 | |
| 97 | 	if (no_family) return EAI_NODATA; |
| 98 | |
| 99 | 	nais = nservs * naddrs; |
| 100 | 	canon_len = strlen(canon); |
| 101 | 	out = calloc(1, nais * sizeof(*out) + canon_len + 1); |
| 102 | 	if (!out) return EAI_MEMORY; |
| 103 | |
| 104 | 	if (canon_len) { |
| 105 | 		outcanon = (void *)&out[nais]; |
| 106 | 		memcpy(outcanon, canon, canon_len+1); |
| 107 | 	} else { |
| 108 | 		outcanon = 0; |
| 109 | 	} |
| 110 | |
| 111 | 	for (k=i=0; i<naddrs; i++) for (j=0; j<nservs; j++, k++) { |
| 112 | 		out[k].slot = k; |
| 113 | 		out[k].ai = (struct addrinfo){ |
| 114 | 			.ai_family = addrs[i].family, |
| 115 | 			.ai_socktype = ports[j].socktype, |
| 116 | 			.ai_protocol = ports[j].proto, |
| 117 | 			.ai_addrlen = addrs[i].family == AF_INET |
| 118 | 				? sizeof(struct sockaddr_in) |
| 119 | 				: sizeof(struct sockaddr_in6), |
| 120 | 			.ai_addr = (void *)&out[k].sa, |
| 121 | 			.ai_canonname = outcanon }; |
| 122 | 		if (k) out[k-1].ai.ai_next = &out[k].ai; |
| 123 | 		switch (addrs[i].family) { |
| 124 | 		case AF_INET: |
| 125 | 			out[k].sa.sin.sin_family = AF_INET; |
| 126 | 			out[k].sa.sin.sin_port = htons(ports[j].port); |
| 127 | 			memcpy(&out[k].sa.sin.sin_addr, &addrs[i].addr, 4); |
| 128 | 			break; |
| 129 | 		case AF_INET6: |
| 130 | 			out[k].sa.sin6.sin6_family = AF_INET6; |
| 131 | 			out[k].sa.sin6.sin6_port = htons(ports[j].port); |
| 132 | 			out[k].sa.sin6.sin6_scope_id = addrs[i].scopeid; |
| 133 | 			memcpy(&out[k].sa.sin6.sin6_addr, &addrs[i].addr, 16); |
| 134 | 			break;			 |
| 135 | 		} |
| 136 | 	} |
| 137 | 	out[0].ref = nais; |
| 138 | 	*res = &out->ai; |
| 139 | 	return 0; |
| 140 | } |