Annotation of embedaddon/sudo/compat/getaddrinfo.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Replacement for a missing getaddrinfo.
3: *
4: * This is an implementation of getaddrinfo for systems that don't have one so
5: * that networking code can use a consistant interface without #ifdef. It is
6: * a fairly minimal implementation, with the following limitations:
7: *
8: * - IPv4 support only. IPv6 is not supported.
9: * - AI_ADDRCONFIG is ignored.
10: * - Not thread-safe due to gethostbyname and getservbyname.
11: * - SOCK_DGRAM and SOCK_STREAM only.
12: * - Multiple possible socket types only generate one addrinfo struct.
13: * - Protocol hints aren't used correctly.
14: *
15: * The last four issues could probably be easily remedied, but haven't been
16: * needed to date. Adding IPv6 support isn't worth it; systems with IPv6
17: * support should already support getaddrinfo natively.
18: *
19: * The canonical version of this file is maintained in the rra-c-util package,
20: * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
21: *
22: * Written by Russ Allbery <rra@stanford.edu>
23: *
24: * The authors hereby relinquish any claim to any copyright that they may have
25: * in this work, whether granted under contract or by operation of law or
26: * international treaty, and hereby commit to the public, at large, that they
27: * shall not, at any time in the future, seek to enforce any copyright in this
28: * work against any person or entity, or prevent any person or entity from
29: * copying, publishing, distributing or creating derivative works of this
30: * work.
31: */
32:
33: #include <config.h>
34:
35: #include <sys/types.h>
36: #include <sys/socket.h>
37:
38: #include <stdio.h>
39: #ifdef STDC_HEADERS
40: # include <stdlib.h>
41: # include <stddef.h>
42: #else
43: # ifdef HAVE_STDLIB_H
44: # include <stdlib.h>
45: # endif
46: #endif /* STDC_HEADERS */
47: #ifdef HAVE_STRING_H
48: # include <string.h>
49: #endif /* HAVE_STRING_H */
50: #ifdef HAVE_STRINGS_H
51: # include <strings.h>
52: #endif /* HAVE_STRINGS_H */
53: #include <netdb.h>
54: #include <errno.h>
55:
56: #include <netinet/in.h>
57:
58: #include "compat/getaddrinfo.h"
59: #include "missing.h"
60:
61: /* We need access to h_errno to map errors from gethostbyname. */
62: #if !HAVE_DECL_H_ERRNO
63: extern int h_errno;
64: #endif
65:
66: /*
67: * The netdb constants, which aren't always defined (particularly if h_errno
68: * isn't declared). We also make sure that a few of the less-used ones are
69: * defined so that we can deal with them in case statements.
70: */
71: #ifndef HOST_NOT_FOUND
72: # define HOST_NOT_FOUND 1
73: # define TRY_AGAIN 2
74: # define NO_RECOVERY 3
75: # define NO_DATA 4
76: #endif
77: #ifndef NETDB_INTERNAL
78: # define NETDB_INTERNAL -1
79: #endif
80:
81: /*
82: * If we're running the test suite, rename the functions to avoid conflicts
83: * with the system version. Note that we don't rename the structures and
84: * constants, but that should be okay (except possibly for gai_strerror).
85: */
86: #if TESTING
87: # define gai_strerror test_gai_strerror
88: # define freeaddrinfo test_freeaddrinfo
89: # define getaddrinfo test_getaddrinfo
90: const char *test_gai_strerror(int);
91: void test_freeaddrinfo(struct addrinfo *);
92: int test_getaddrinfo(const char *, const char *, const struct addrinfo *,
93: struct addrinfo **);
94: #endif
95:
96: /*
97: * If the native platform doesn't support AI_NUMERICSERV or AI_NUMERICHOST,
98: * pick some other values for them.
99: */
100: #if TESTING
101: # if AI_NUMERICSERV == 0
102: # undef AI_NUMERICSERV
103: # define AI_NUMERICSERV 0x0080
104: # endif
105: # if AI_NUMERICHOST == 0
106: # undef AI_NUMERICHOST
107: # define AI_NUMERICHOST 0x0100
108: # endif
109: #endif
110:
111: /*
112: * Value representing all of the hint flags set. Linux uses flags up to
113: * 0x0400, so be sure not to break when testing on that platform.
114: */
115: #if TESTING
116: # ifdef HAVE_GETADDRINFO
117: # define AI_INTERNAL_ALL 0x04ff
118: # else
119: # define AI_INTERNAL_ALL 0x01ff
120: # endif
121: #else
122: # define AI_INTERNAL_ALL 0x007f
123: #endif
124:
125: /* Table of strings corresponding to the EAI_* error codes. */
126: static const char * const gai_errors[] = {
127: "Host name lookup failure", /* 1 EAI_AGAIN */
128: "Invalid flag value", /* 2 EAI_BADFLAGS */
129: "Unknown server error", /* 3 EAI_FAIL */
130: "Unsupported address family", /* 4 EAI_FAMILY */
131: "Memory allocation failure", /* 5 EAI_MEMORY */
132: "Host unknown or not given", /* 6 EAI_NONAME */
133: "Service not supported for socket", /* 7 EAI_SERVICE */
134: "Unsupported socket type", /* 8 EAI_SOCKTYPE */
135: "System error", /* 9 EAI_SYSTEM */
136: "Supplied buffer too small", /* 10 EAI_OVERFLOW */
137: };
138:
139: /* Macro to set the len attribute of sockaddr_in. */
140: #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
141: # define sin_set_length(s) ((s)->sin_len = sizeof(struct sockaddr_in))
142: #else
143: # define sin_set_length(s) /* empty */
144: #endif
145:
146: /*
147: * Used for iterating through arrays. ARRAY_SIZE returns the number of
148: * elements in the array (useful for a < upper bound in a for loop).
149: */
150: #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
151:
152:
153: /*
154: * Return a constant string for a given EAI_* error code or a string
155: * indicating an unknown error.
156: */
157: const char *
158: gai_strerror(int ecode)
159: {
160: if (ecode < 1 || (size_t) ecode > ARRAY_SIZE(gai_errors))
161: return "Unknown error";
162: else
163: return gai_errors[ecode - 1];
164: }
165:
166:
167: /*
168: * Free a linked list of addrinfo structs.
169: */
170: void
171: freeaddrinfo(struct addrinfo *ai)
172: {
173: struct addrinfo *next;
174:
175: while (ai != NULL) {
176: next = ai->ai_next;
177: if (ai->ai_addr != NULL)
178: free(ai->ai_addr);
179: if (ai->ai_canonname != NULL)
180: free(ai->ai_canonname);
181: free(ai);
182: ai = next;
183: }
184: }
185:
186:
187: /*
188: * Convert a numeric service string to a number with error checking, returning
189: * true if the number was parsed correctly and false otherwise. Stores the
190: * converted number in the second argument. Equivalent to calling strtol, but
191: * with the base always fixed at 10, with checking of errno, ensuring that all
192: * of the string is consumed, and checking that the resulting number is
193: * positive.
194: */
195: static int
196: convert_service(const char *string, long *result)
197: {
198: char *end;
199:
200: if (*string == '\0')
201: return 0;
202: errno = 0;
203: *result = strtol(string, &end, 10);
204: if (errno != 0 || *end != '\0' || *result < 0)
205: return 0;
206: return 1;
207: }
208:
209:
210: /*
211: * Allocate a new addrinfo struct, setting some defaults given that this
212: * implementation is IPv4 only. Also allocates an attached sockaddr_in and
213: * zeroes it, per the requirement for getaddrinfo. Takes the socktype,
214: * canonical name (which is copied if not NULL), address, and port. Returns
215: * NULL on a memory allocation failure.
216: */
217: static struct addrinfo *
218: gai_addrinfo_new(int socktype, const char *canonical, struct in_addr addr,
219: unsigned short port)
220: {
221: struct addrinfo *ai;
222:
223: ai = malloc(sizeof(*ai));
224: if (ai == NULL)
225: return NULL;
226: ai->ai_addr = malloc(sizeof(struct sockaddr_in));
227: if (ai->ai_addr == NULL) {
228: free(ai);
229: return NULL;
230: }
231: ai->ai_next = NULL;
232: if (canonical == NULL)
233: ai->ai_canonname = NULL;
234: else {
235: ai->ai_canonname = strdup(canonical);
236: if (ai->ai_canonname == NULL) {
237: freeaddrinfo(ai);
238: return NULL;
239: }
240: }
241: memset(ai->ai_addr, 0, sizeof(struct sockaddr_in));
242: ai->ai_flags = 0;
243: ai->ai_family = AF_INET;
244: ai->ai_socktype = socktype;
245: ai->ai_protocol = (socktype == SOCK_DGRAM) ? IPPROTO_UDP : IPPROTO_TCP;
246: ai->ai_addrlen = sizeof(struct sockaddr_in);
247: ((struct sockaddr_in *) ai->ai_addr)->sin_family = AF_INET;
248: ((struct sockaddr_in *) ai->ai_addr)->sin_addr = addr;
249: ((struct sockaddr_in *) ai->ai_addr)->sin_port = htons(port);
250: sin_set_length((struct sockaddr_in *) ai->ai_addr);
251: return ai;
252: }
253:
254:
255: /*
256: * Look up a service. Takes the service name (which may be numeric), the hint
257: * flags, a pointer to the socket type (used to determine whether TCP or UDP
258: * services are of interest and, if 0, is filled in with the result of
259: * getservbyname if the service was not numeric), and a pointer to the
260: * addrinfo struct to fill in. Returns 0 on success or an EAI_* error on
261: * failure.
262: */
263: static int
264: gai_service(const char *servname, int flags, int *type, unsigned short *port)
265: {
266: struct servent *servent;
267: const char *protocol;
268: long value;
269:
270: if (convert_service(servname, &value)) {
271: if (value > (1L << 16) - 1)
272: return EAI_SERVICE;
273: *port = value;
274: } else {
275: if (flags & AI_NUMERICSERV)
276: return EAI_NONAME;
277: if (*type != 0)
278: protocol = (*type == SOCK_DGRAM) ? "udp" : "tcp";
279: else
280: protocol = NULL;
281:
282: /*
283: * We really technically should be generating an addrinfo struct for
284: * each possible protocol unless type is set, but this works well
285: * enough for what I need this for.
286: */
287: servent = getservbyname(servname, protocol);
288: if (servent == NULL)
289: return EAI_NONAME;
290: if (strcmp(servent->s_proto, "udp") == 0)
291: *type = SOCK_DGRAM;
292: else if (strcmp(servent->s_proto, "tcp") == 0)
293: *type = SOCK_STREAM;
294: else
295: return EAI_SERVICE;
296: *port = htons(servent->s_port);
297: }
298: return 0;
299: }
300:
301:
302: /*
303: * Look up a host and fill in a linked list of addrinfo structs with the
304: * results, one per IP address of the returned host. Takes the name or IP
305: * address of the host as a string, the lookup flags, the type of socket (to
306: * fill into the addrinfo structs), the port (likewise), and a pointer to
307: * where the head of the linked list should be put. Returns 0 on success or
308: * the appropriate EAI_* error.
309: */
310: static int
311: gai_lookup(const char *nodename, int flags, int socktype, unsigned short port,
312: struct addrinfo **res)
313: {
314: struct addrinfo *ai, *first, *prev;
315: struct in_addr addr;
316: struct hostent *host;
317: const char *canonical;
318: int i;
319:
320: if (inet_aton(nodename, &addr)) {
321: canonical = (flags & AI_CANONNAME) ? nodename : NULL;
322: ai = gai_addrinfo_new(socktype, canonical, addr, port);
323: if (ai == NULL)
324: return EAI_MEMORY;
325: *res = ai;
326: return 0;
327: } else {
328: if (flags & AI_NUMERICHOST)
329: return EAI_NONAME;
330: host = gethostbyname(nodename);
331: if (host == NULL)
332: switch (h_errno) {
333: case HOST_NOT_FOUND:
334: return EAI_NONAME;
335: case TRY_AGAIN:
336: case NO_DATA:
337: return EAI_AGAIN;
338: case NO_RECOVERY:
339: return EAI_FAIL;
340: case NETDB_INTERNAL:
341: default:
342: return EAI_SYSTEM;
343: }
344: if (host->h_addr_list[0] == NULL)
345: return EAI_FAIL;
346: canonical = (flags & AI_CANONNAME)
347: ? ((host->h_name != NULL) ? host->h_name : nodename)
348: : NULL;
349: first = NULL;
350: prev = NULL;
351: for (i = 0; host->h_addr_list[i] != NULL; i++) {
352: if (host->h_length != sizeof(addr)) {
353: freeaddrinfo(first);
354: return EAI_FAIL;
355: }
356: memcpy(&addr, host->h_addr_list[i], sizeof(addr));
357: ai = gai_addrinfo_new(socktype, canonical, addr, port);
358: if (ai == NULL) {
359: freeaddrinfo(first);
360: return EAI_MEMORY;
361: }
362: if (first == NULL) {
363: first = ai;
364: prev = ai;
365: } else {
366: prev->ai_next = ai;
367: prev = ai;
368: }
369: }
370: *res = first;
371: return 0;
372: }
373: }
374:
375:
376: /*
377: * The actual getaddrinfo implementation.
378: */
379: int
380: getaddrinfo(const char *nodename, const char *servname,
381: const struct addrinfo *hints, struct addrinfo **res)
382: {
383: struct addrinfo *ai;
384: struct in_addr addr;
385: int flags, socktype, status;
386: unsigned short port;
387:
388: /* Take the hints into account and check them for validity. */
389: if (hints != NULL) {
390: flags = hints->ai_flags;
391: socktype = hints->ai_socktype;
392: if ((flags & AI_INTERNAL_ALL) != flags)
393: return EAI_BADFLAGS;
394: if (hints->ai_family != AF_UNSPEC && hints->ai_family != AF_INET)
395: return EAI_FAMILY;
396: if (socktype != 0 && socktype != SOCK_STREAM && socktype != SOCK_DGRAM)
397: return EAI_SOCKTYPE;
398:
399: /* EAI_SOCKTYPE isn't quite right, but there isn't anything better. */
400: if (hints->ai_protocol != 0) {
401: int protocol = hints->ai_protocol;
402: if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
403: return EAI_SOCKTYPE;
404: }
405: } else {
406: flags = 0;
407: socktype = 0;
408: }
409:
410: /*
411: * See what we're doing. If nodename is null, either AI_PASSIVE is set or
412: * we're getting information for connecting to a service on the loopback
413: * address. Otherwise, we're getting information for connecting to a
414: * remote system.
415: */
416: if (servname == NULL)
417: port = 0;
418: else {
419: status = gai_service(servname, flags, &socktype, &port);
420: if (status != 0)
421: return status;
422: }
423: if (nodename != NULL)
424: return gai_lookup(nodename, flags, socktype, port, res);
425: else {
426: if (servname == NULL)
427: return EAI_NONAME;
428: if ((flags & AI_PASSIVE) == AI_PASSIVE)
429: addr.s_addr = INADDR_ANY;
430: else
431: addr.s_addr = htonl(0x7f000001UL);
432: ai = gai_addrinfo_new(socktype, NULL, addr, port);
433: if (ai == NULL)
434: return EAI_MEMORY;
435: *res = ai;
436: return 0;
437: }
438: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>