source: web/old/remctl-2.14/portable/getaddrinfo.c @ f6f3e91

web
Last change on this file since f6f3e91 was f6f3e91, checked in by Jessica B. Hamrick <jhamrick@…>, 15 years ago

Preserve directory hierarchy (not sure what happened to it)

  • Property mode set to 100644
File size: 11.7 KB
Line 
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 * Written by Russ Allbery <rra@stanford.edu>
20 * This work is hereby placed in the public domain by its author.
21 */
22
23#include <config.h>
24#include <portable/system.h>
25#include <portable/socket.h>
26
27#include <errno.h>
28
29/*
30 * If we're running the test suite, rename the functions to avoid conflicts
31 * with the system version.  Note that we don't rename the structures and
32 * constants, but that should be okay (except possibly for gai_strerror).
33 */
34#if TESTING
35# define gai_strerror test_gai_strerror
36# define freeaddrinfo test_freeaddrinfo
37# define getaddrinfo  test_getaddrinfo
38const char *test_gai_strerror(int);
39void test_freeaddrinfo(struct addrinfo *);
40int test_getaddrinfo(const char *, const char *, const struct addrinfo *,
41                     struct addrinfo **);
42
43/*
44 * If the native platform doesn't support AI_NUMERICSERV or AI_NUMERICHOST,
45 * pick some other values for them.
46 */
47# if AI_NUMERICSERV == 0
48#  undef AI_NUMERICSERV
49#  define AI_NUMERICSERV 0x0080
50# endif
51# if AI_NUMERICHOST == 0
52#  undef AI_NUMERICHOST
53#  define AI_NUMERICHOST 0x0100
54# endif
55#endif
56
57/* Table of strings corresponding to the EAI_* error codes. */
58static const char * const gai_errors[] = {
59    "Host name lookup failure",         /*  1 EAI_AGAIN */
60    "Invalid flag value",               /*  2 EAI_BADFLAGS */
61    "Unknown server error",             /*  3 EAI_FAIL */
62    "Unsupported address family",       /*  4 EAI_FAMILY */
63    "Memory allocation failure",        /*  5 EAI_MEMORY */
64    "Host unknown or not given",        /*  6 EAI_NONAME */
65    "Service not supported for socket", /*  7 EAI_SERVICE */
66    "Unsupported socket type",          /*  8 EAI_SOCKTYPE */
67    "System error",                     /*  9 EAI_SYSTEM */
68    "Supplied buffer too small",        /* 10 EAI_OVERFLOW */
69};
70
71/*
72 * Value representing all of the hint flags set.  Linux uses flags up to
73 * 0x0400, so be sure not to break when testing on that platform.
74 */
75#if TESTING
76# ifdef HAVE_GETADDRINFO
77#  define AI_INTERNAL_ALL 0x04ff
78# else
79#  define AI_INTERNAL_ALL 0x01ff
80# endif
81#else
82# define AI_INTERNAL_ALL 0x007f
83#endif
84
85/* Macro to set the len attribute of sockaddr_in. */
86#if HAVE_STRUCT_SOCKADDR_SA_LEN
87# define sin_set_length(s)      ((s)->sin_len  = sizeof(struct sockaddr_in))
88#else
89# define sin_set_length(s)      /* empty */
90#endif
91
92/*
93 * Used for iterating through arrays.  ARRAY_SIZE returns the number of
94 * elements in the array (useful for a < upper bound in a for loop).
95 */
96#define ARRAY_SIZE(array)       (sizeof(array) / sizeof((array)[0]))
97
98
99/*
100 * Return a constant string for a given EAI_* error code or a string
101 * indicating an unknown error.
102 */
103const char *
104gai_strerror(int ecode)
105{
106    if (ecode < 1 || (size_t) ecode > ARRAY_SIZE(gai_errors))
107        return "Unknown error";
108    else
109        return gai_errors[ecode - 1];
110}
111
112
113/*
114 * Free a linked list of addrinfo structs.
115 */
116void
117freeaddrinfo(struct addrinfo *ai)
118{
119    struct addrinfo *next;
120
121    while (ai != NULL) {
122        next = ai->ai_next;
123        if (ai->ai_addr != NULL)
124            free(ai->ai_addr);
125        if (ai->ai_canonname != NULL)
126            free(ai->ai_canonname);
127        free(ai);
128        ai = next;
129    }
130}
131
132
133/*
134 * Convert a numeric service string to a number with error checking, returning
135 * true if the number was parsed correctly and false otherwise.  Stores the
136 * converted number in the second argument.  Equivalent to calling strtol, but
137 * with the base always fixed at 10, with checking of errno, ensuring that all
138 * of the string is consumed, and checking that the resulting number is
139 * positive.
140 */
141static int
142convert_service(const char *string, long *result)
143{
144    char *end;
145
146    if (*string == '\0')
147        return 0;
148    errno = 0;
149    *result = strtol(string, &end, 10);
150    if (errno != 0 || *end != '\0' || *result < 0)
151        return 0;
152    return 1;
153}
154
155
156/*
157 * Allocate a new addrinfo struct, setting some defaults given that this
158 * implementation is IPv4 only.  Also allocates an attached sockaddr_in and
159 * zeroes it, per the requirement for getaddrinfo.  Takes the socktype,
160 * canonical name (which is copied if not NULL), address, and port.  Returns
161 * NULL on a memory allocation failure.
162 */
163static struct addrinfo *
164gai_addrinfo_new(int socktype, const char *canonical, struct in_addr addr,
165                 unsigned short port)
166{
167    struct addrinfo *ai;
168
169    ai = malloc(sizeof(*ai));
170    if (ai == NULL)
171        return NULL;
172    ai->ai_addr = malloc(sizeof(struct sockaddr_in));
173    if (ai->ai_addr == NULL) {
174        free(ai);
175        return NULL;
176    }
177    ai->ai_next = NULL;
178    if (canonical == NULL)
179        ai->ai_canonname = NULL;
180    else {
181        ai->ai_canonname = strdup(canonical);
182        if (ai->ai_canonname == NULL) {
183            freeaddrinfo(ai);
184            return NULL;
185        }
186    }
187    memset(ai->ai_addr, 0, sizeof(struct sockaddr_in));
188    ai->ai_flags = 0;
189    ai->ai_family = AF_INET;
190    ai->ai_socktype = socktype;
191    ai->ai_protocol = (socktype == SOCK_DGRAM) ? IPPROTO_UDP : IPPROTO_TCP;
192    ai->ai_addrlen = sizeof(struct sockaddr_in);
193    ((struct sockaddr_in *) ai->ai_addr)->sin_family = AF_INET;
194    ((struct sockaddr_in *) ai->ai_addr)->sin_addr = addr;
195    ((struct sockaddr_in *) ai->ai_addr)->sin_port = htons(port);
196    sin_set_length((struct sockaddr_in *) ai->ai_addr);
197    return ai;
198}
199
200
201/*
202 * Look up a service.  Takes the service name (which may be numeric), the hint
203 * flags, a pointer to the socket type (used to determine whether TCP or UDP
204 * services are of interest and, if 0, is filled in with the result of
205 * getservbyname if the service was not numeric), and a pointer to the
206 * addrinfo struct to fill in.  Returns 0 on success or an EAI_* error on
207 * failure.
208 */
209static int
210gai_service(const char *servname, int flags, int *type, unsigned short *port)
211{
212    struct servent *servent;
213    const char *protocol;
214    long value;
215
216    if (convert_service(servname, &value)) {
217        if (value > (1L << 16) - 1)
218            return EAI_SERVICE;
219        *port = value;
220    } else {
221        if (flags & AI_NUMERICSERV)
222            return EAI_NONAME;
223        if (*type != 0)
224            protocol = (*type == SOCK_DGRAM) ? "udp" : "tcp";
225        else
226            protocol = NULL;
227
228        /*
229         * We really technically should be generating an addrinfo struct for
230         * each possible protocol unless type is set, but this works well
231         * enough for what I need this for.
232         */
233        servent = getservbyname(servname, protocol);
234        if (servent == NULL)
235            return EAI_NONAME;
236        if (strcmp(servent->s_proto, "udp") == 0)
237            *type = SOCK_DGRAM;
238        else if (strcmp(servent->s_proto, "tcp") == 0)
239            *type = SOCK_STREAM;
240        else
241            return EAI_SERVICE;
242        *port = htons(servent->s_port);
243    }
244    return 0;
245}
246
247
248/*
249 * Look up a host and fill in a linked list of addrinfo structs with the
250 * results, one per IP address of the returned host.  Takes the name or IP
251 * address of the host as a string, the lookup flags, the type of socket (to
252 * fill into the addrinfo structs), the port (likewise), and a pointer to
253 * where the head of the linked list should be put.  Returns 0 on success or
254 * the appropriate EAI_* error.
255 */
256static int
257gai_lookup(const char *nodename, int flags, int socktype, unsigned short port,
258           struct addrinfo **res)
259{
260    struct addrinfo *ai, *first, *prev;
261    struct in_addr addr;
262    struct hostent *host;
263    const char *canonical;
264    int i;
265
266    if (inet_aton(nodename, &addr)) {
267        canonical = (flags & AI_CANONNAME) ? nodename : NULL;
268        ai = gai_addrinfo_new(socktype, canonical, addr, port);
269        if (ai == NULL)
270            return EAI_MEMORY;
271        *res = ai;
272        return 0;
273    } else {
274        if (flags & AI_NUMERICHOST)
275            return EAI_NONAME;
276        host = gethostbyname(nodename);
277        if (host == NULL)
278            switch (h_errno) {
279            case HOST_NOT_FOUND:
280                return EAI_NONAME;
281            case TRY_AGAIN:
282            case NO_DATA:
283                return EAI_AGAIN;
284            case NO_RECOVERY:
285                return EAI_FAIL;
286            case NETDB_INTERNAL:
287            default:
288                return EAI_SYSTEM;
289            }
290        if (host->h_addr_list[0] == NULL)
291            return EAI_FAIL;
292        canonical = (flags & AI_CANONNAME)
293            ? ((host->h_name != NULL) ? host->h_name : nodename)
294            : NULL;
295        first = NULL;
296        prev = NULL;
297        for (i = 0; host->h_addr_list[i] != NULL; i++) {
298            if (host->h_length != sizeof(addr)) {
299                freeaddrinfo(first);
300                return EAI_FAIL;
301            }
302            memcpy(&addr, host->h_addr_list[i], sizeof(addr));
303            ai = gai_addrinfo_new(socktype, canonical, addr, port);
304            if (ai == NULL) {
305                freeaddrinfo(first);
306                return EAI_MEMORY;
307            }
308            if (first == NULL) {
309                first = ai;
310                prev = ai;
311            } else {
312                prev->ai_next = ai;
313                prev = ai;
314            }
315        }
316        *res = first;
317        return 0;
318    }
319}
320
321
322/*
323 * The actual getaddrinfo implementation.
324 */
325int
326getaddrinfo(const char *nodename, const char *servname,
327            const struct addrinfo *hints, struct addrinfo **res)
328{
329    struct addrinfo *ai;
330    struct in_addr addr;
331    int flags, socktype, status;
332    unsigned short port;
333
334    /* Take the hints into account and check them for validity. */
335    if (hints != NULL) {
336        flags = hints->ai_flags;
337        socktype = hints->ai_socktype;
338        if ((flags & AI_INTERNAL_ALL) != flags)
339            return EAI_BADFLAGS;
340        if (hints->ai_family != AF_UNSPEC && hints->ai_family != AF_INET)
341            return EAI_FAMILY;
342        if (socktype != 0 && socktype != SOCK_STREAM && socktype != SOCK_DGRAM)
343            return EAI_SOCKTYPE;
344
345        /* EAI_SOCKTYPE isn't quite right, but there isn't anything better. */
346        if (hints->ai_protocol != 0) {
347            int protocol = hints->ai_protocol;
348            if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
349                return EAI_SOCKTYPE;
350        }
351    } else {
352        flags = 0;
353        socktype = 0;
354    }
355
356    /*
357     * See what we're doing.  If nodename is null, either AI_PASSIVE is set or
358     * we're getting information for connecting to a service on the loopback
359     * address.  Otherwise, we're getting information for connecting to a
360     * remote system.
361     */
362    if (servname == NULL)
363        port = 0;
364    else {
365        status = gai_service(servname, flags, &socktype, &port);
366        if (status != 0)
367            return status;
368    }
369    if (nodename != NULL)
370        return gai_lookup(nodename, flags, socktype, port, res);
371    else {
372        if (servname == NULL)
373            return EAI_NONAME;
374        addr.s_addr = (flags & AI_PASSIVE) ? INADDR_ANY : INADDR_LOOPBACK;
375        ai = gai_addrinfo_new(socktype, NULL, addr, port);
376        if (ai == NULL)
377            return EAI_MEMORY;
378        *res = ai;
379        return 0;
380    }
381}
Note: See TracBrowser for help on using the repository browser.