source: web/old/remctl-2.14/util/network.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: 15.6 KB
Line 
1/*
2 * Utility functions for network connections.
3 *
4 * This is a collection of utility functions for network connections and
5 * socket creation, encapsulating some of the complexities of IPv4 and IPv6
6 * support and abstracting operations common to most network code.
7 *
8 * All of the portability difficulties with supporting IPv4 and IPv6 should be
9 * encapsulated in the combination of this code and replacement
10 * implementations for functions that aren't found on some pre-IPv6 systems.
11 * No other part of remctl should have to care about IPv4 vs. IPv6.
12 *
13 * Copyright (c) 2004, 2005, 2006, 2007, 2008
14 *     by Internet Systems Consortium, Inc. ("ISC")
15 * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
16 *     2002, 2003 by The Internet Software Consortium and Rich Salz
17 *
18 * See LICENSE for licensing terms.
19 */
20
21#include <config.h>
22#include <portable/system.h>
23#include <portable/socket.h>
24
25#include <errno.h>
26
27#include <util/util.h>
28
29/* Macros to set the len attribute of sockaddrs. */
30#if HAVE_STRUCT_SOCKADDR_SA_LEN
31# define sin_set_length(s)      ((s)->sin_len  = sizeof(struct sockaddr_in))
32# define sin6_set_length(s)     ((s)->sin6_len = sizeof(struct sockaddr_in6))
33#else
34# define sin_set_length(s)      /* empty */
35# define sin6_set_length(s)     /* empty */
36#endif
37
38/* If SO_REUSEADDR isn't available, make calls to set_reuseaddr go away. */
39#ifndef SO_REUSEADDR
40# define network_set_reuseaddr(fd)      /* empty */
41#endif
42
43
44/*
45 * Set SO_REUSEADDR on a socket if possible (so that something new can listen
46 * on the same port immediately if the daemon dies unexpectedly).
47 */
48#ifdef SO_REUSEADDR
49static void
50network_set_reuseaddr(int fd)
51{
52    int flag = 1;
53
54    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) < 0)
55        syswarn("cannot mark bind address reusable");
56}
57#endif
58
59
60/*
61 * Create an IPv4 socket and bind it, returning the resulting file descriptor
62 * (or -1 on a failure).
63 */
64int
65network_bind_ipv4(const char *address, unsigned short port)
66{
67    int fd;
68    struct sockaddr_in server;
69    struct in_addr addr;
70
71    /* Create the socket. */
72    fd = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
73    if (fd < 0) {
74        syswarn("cannot create IPv4 socket for %s,%hu", address, port);
75        return -1;
76    }
77    network_set_reuseaddr(fd);
78
79    /* Accept "any" or "all" in the bind address to mean 0.0.0.0. */
80    if (!strcmp(address, "any") || !strcmp(address, "all"))
81        address = "0.0.0.0";
82
83    /* Flesh out the socket and do the bind. */
84    server.sin_family = AF_INET;
85    server.sin_port = htons(port);
86    if (!inet_aton(address, &addr)) {
87        warn("invalid IPv4 address %s", address);
88        return -1;
89    }
90    server.sin_addr = addr;
91    sin_set_length(&server);
92    if (bind(fd, (struct sockaddr *) &server, sizeof(server)) < 0) {
93        syswarn("cannot bind socket for %s,%hu", address, port);
94        return -1;
95    }
96    return fd;
97}
98
99
100/*
101 * Create an IPv6 socket and bind it, returning the resulting file descriptor
102 * (or -1 on a failure).  Note that we don't warn (but still return failure)
103 * if the reason for the socket creation failure is that IPv6 isn't supported;
104 * this is to handle systems like many Linux hosts where IPv6 is available in
105 * userland but the kernel doesn't support it.
106 */
107#if HAVE_INET6
108int
109network_bind_ipv6(const char *address, unsigned short port)
110{
111    int fd;
112    struct sockaddr_in6 server;
113    struct in6_addr addr;
114
115    /* Create the socket. */
116    fd = socket(PF_INET6, SOCK_STREAM, IPPROTO_IP);
117    if (fd < 0) {
118        if (socket_errno != EAFNOSUPPORT && socket_errno != EPROTONOSUPPORT)
119            syswarn("cannot create IPv6 socket for %s,%hu", address, port);
120        return -1;
121    }
122    network_set_reuseaddr(fd);
123
124    /* Accept "any" or "all" in the bind address to mean 0.0.0.0. */
125    if (!strcmp(address, "any") || !strcmp(address, "all"))
126        address = "::";
127
128    /* Flesh out the socket and do the bind. */
129    server.sin6_family = AF_INET6;
130    server.sin6_port = htons(port);
131    if (inet_pton(AF_INET6, address, &addr) < 1) {
132        warn("invalid IPv6 address %s", address);
133        socket_close(fd);
134        return -1;
135    }
136    server.sin6_addr = addr;
137    sin6_set_length(&server);
138    if (bind(fd, (struct sockaddr *) &server, sizeof(server)) < 0) {
139        syswarn("cannot bind socket for %s,%hu", address, port);
140        socket_close(fd);
141        return -1;
142    }
143    return fd;
144}
145#else /* HAVE_INET6 */
146int
147network_bind_ipv6(const char *address, unsigned short port)
148{
149    warn("cannot bind %s,%hu: not built with IPv6 support", address, port);
150    return -1;
151}
152#endif /* HAVE_INET6 */
153
154
155/*
156 * Create and bind sockets for every local address, as determined by
157 * getaddrinfo if IPv6 is available (otherwise, just use the IPv4 loopback
158 * address).  Takes the port number, and then a pointer to an array of
159 * integers and a pointer to a count of them.  Allocates a new array to hold
160 * the file descriptors and stores the count in the third argument.
161 */
162#if HAVE_INET6
163void
164network_bind_all(unsigned short port, int **fds, int *count)
165{
166    struct addrinfo hints, *addrs, *addr;
167    int error, fd, size;
168    char service[16], name[INET6_ADDRSTRLEN];
169
170    *count = 0;
171
172    /* Do the query to find all the available addresses. */
173    memset(&hints, 0, sizeof(hints));
174    hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
175    hints.ai_family = AF_UNSPEC;
176    hints.ai_socktype = SOCK_STREAM;
177    snprintf(service, sizeof(service), "%hu", port);
178    error = getaddrinfo(NULL, service, &hints, &addrs);
179    if (error < 0) {
180        warn("getaddrinfo failed: %s", gai_strerror(error));
181        return;
182    }
183
184    /*
185     * Now, try to bind each of them.  Start the fds array at two entries,
186     * assuming an IPv6 and IPv4 socket, and grow it by two when necessary.
187     */
188    size = 2;
189    *fds = xmalloc(size * sizeof(int));
190    for (addr = addrs; addr != NULL; addr = addr->ai_next) {
191        network_sockaddr_sprint(name, sizeof(name), addr->ai_addr);
192        if (addr->ai_family == AF_INET)
193            fd = network_bind_ipv4(name, port);
194        else if (addr->ai_family == AF_INET6)
195            fd = network_bind_ipv6(name, port);
196        else
197            continue;
198        if (fd >= 0) {
199            if (*count >= size) {
200                size += 2;
201                *fds = xrealloc(*fds, size * sizeof(int));
202            }
203            (*fds)[*count] = fd;
204            (*count)++;
205        }
206    }
207    freeaddrinfo(addrs);
208}
209#else /* HAVE_INET6 */
210void
211network_bind_all(unsigned short port, int **fds, int *count)
212{
213    int fd;
214
215    fd = network_bind_ipv4("0.0.0.0", port);
216    if (fd >= 0) {
217        *fds = xmalloc(sizeof(int));
218        *fds[0] = fd;
219        *count = 1;
220    } else {
221        *fds = NULL;
222        *count = 0;
223    }
224}
225#endif /* HAVE_INET6 */
226
227
228/*
229 * Binds the given socket to an appropriate source address for its family,
230 * using innconf information or the provided source address.  Returns true on
231 * success and false on failure.
232 */
233static int
234network_source(int fd, int family, const char *source)
235{
236    if (source == NULL)
237        return 1;
238    if (family == AF_INET) {
239        struct sockaddr_in saddr;
240
241        if (source == NULL || strcmp(source, "all") == 0)
242            return 1;
243        memset(&saddr, 0, sizeof(saddr));
244        saddr.sin_family = AF_INET;
245        if (!inet_aton(source, &saddr.sin_addr))
246            return 0;
247        return bind(fd, (struct sockaddr *) &saddr, sizeof(saddr)) == 0;
248    }
249#ifdef HAVE_INET6
250    else if (family == AF_INET6) {
251        struct sockaddr_in6 saddr;
252
253        if (source == NULL || strcmp(source, "all") == 0)
254            return 1;
255        memset(&saddr, 0, sizeof(saddr));
256        saddr.sin6_family = AF_INET6;
257        if (inet_pton(AF_INET6, source, &saddr.sin6_addr) < 1)
258            return 0;
259        return bind(fd, (struct sockaddr *) &saddr, sizeof(saddr)) == 0;
260    }
261#endif
262    else
263        return 1;
264}
265
266
267/*
268 * Given a linked list of addrinfo structs representing the remote service,
269 * try to create a local socket and connect to that service.  Takes an
270 * optional source address.  Try each address in turn until one of them
271 * connects.  Returns the file descriptor of the open socket on success, or -1
272 * on failure.  Tries to leave the reason for the failure in errno.
273 */
274int
275network_connect(struct addrinfo *ai, const char *source)
276{
277    int fd = -1;
278    int oerrno;
279    int success;
280
281    for (success = 0; ai != NULL; ai = ai->ai_next) {
282        if (fd >= 0)
283            socket_close(fd);
284        fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
285        if (fd < 0)
286            continue;
287        if (!network_source(fd, ai->ai_family, source))
288            continue;
289        if (connect(fd, ai->ai_addr, ai->ai_addrlen) == 0) {
290            success = 1;
291            break;
292        }
293    }
294    if (success)
295        return fd;
296    else {
297        if (fd >= 0) {
298            oerrno = socket_errno;
299            socket_close(fd);
300            socket_set_errno(oerrno);
301        }
302        return -1;
303    }
304}
305
306
307/*
308 * Like network_connect, but takes a host and a port instead of an addrinfo
309 * struct list.  Returns the file descriptor of the open socket on success, or
310 * -1 on failure.  If getaddrinfo fails, errno may not be set to anything
311 * useful.
312 */
313int
314network_connect_host(const char *host, unsigned short port,
315                     const char *source)
316{
317    struct addrinfo hints, *ai;
318    char portbuf[16];
319    int fd, oerrno;
320
321    memset(&hints, 0, sizeof(hints));
322    hints.ai_family = AF_UNSPEC;
323    hints.ai_socktype = SOCK_STREAM;
324    snprintf(portbuf, sizeof(portbuf), "%d", port);
325    if (getaddrinfo(host, portbuf, &hints, &ai) != 0)
326        return -1;
327    fd = network_connect(ai, source);
328    oerrno = socket_errno;
329    freeaddrinfo(ai);
330    socket_set_errno(oerrno);
331    return fd;
332}
333
334
335/*
336 * Create a new socket of the specified domain and type and do the binding as
337 * if we were a regular client socket, but then return before connecting.
338 * Returns the file descriptor of the open socket on success, or -1 on
339 * failure.  Intended primarily for the use of clients that will then go on to
340 * do a non-blocking connect.
341 */
342int
343network_client_create(int domain, int type, const char *source)
344{
345    int fd, oerrno;
346
347    fd = socket(domain, type, 0);
348    if (fd < 0)
349        return -1;
350    if (!network_source(fd, domain, source)) {
351        oerrno = socket_errno;
352        socket_close(fd);
353        socket_set_errno(oerrno);
354        return -1;
355    }
356    return fd;
357}
358
359
360/*
361 * Print an ASCII representation of the address of the given sockaddr into the
362 * provided buffer.  This buffer must hold at least INET_ADDRSTRLEN characters
363 * for IPv4 addresses and INET6_ADDRSTRLEN characters for IPv6, so generally
364 * it should always be as large as the latter.  Returns success or failure.
365 */
366bool
367network_sockaddr_sprint(char *dst, size_t size, const struct sockaddr *addr)
368{
369    const char *result;
370
371#ifdef HAVE_INET6
372    if (addr->sa_family == AF_INET6) {
373        const struct sockaddr_in6 *sin6;
374
375        sin6 = (const struct sockaddr_in6 *) addr;
376        if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
377            struct in_addr in;
378
379            memcpy(&in, sin6->sin6_addr.s6_addr + 12, sizeof(in));
380            result = inet_ntop(AF_INET, &in, dst, size);
381        } else
382            result = inet_ntop(AF_INET6, &sin6->sin6_addr, dst, size);
383        return (result != NULL);
384    }
385#endif
386    if (addr->sa_family == AF_INET) {
387        const struct sockaddr_in *sin;
388
389        sin = (const struct sockaddr_in *) addr;
390        result = inet_ntop(AF_INET, &sin->sin_addr, dst, size);
391        return (result != NULL);
392    } else {
393        socket_set_errno(EAFNOSUPPORT);
394        return false;
395    }
396}
397
398
399/*
400 * Compare the addresses from two sockaddrs and see whether they're equal.
401 * IPv4 addresses that have been mapped to IPv6 addresses compare equal to the
402 * corresponding IPv4 address.
403 */
404bool
405network_sockaddr_equal(const struct sockaddr *a, const struct sockaddr *b)
406{
407    const struct sockaddr_in *a4 = (const struct sockaddr_in *) a;
408    const struct sockaddr_in *b4 = (const struct sockaddr_in *) b;
409
410#ifdef HAVE_INET6
411    const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) a;
412    const struct sockaddr_in6 *b6 = (const struct sockaddr_in6 *) b;
413    const struct sockaddr *tmp;
414
415    if (a->sa_family == AF_INET && b->sa_family == AF_INET6) {
416        tmp = a;
417        a = b;
418        b = tmp;
419        a6 = (const struct sockaddr_in6 *) a;
420        b4 = (const struct sockaddr_in *) b;
421    }
422    if (a->sa_family == AF_INET6) {
423        if (b->sa_family == AF_INET6)
424            return IN6_ARE_ADDR_EQUAL(&a6->sin6_addr, &b6->sin6_addr);
425        else if (b->sa_family != AF_INET)
426            return false;
427        else if (!IN6_IS_ADDR_V4MAPPED(&a6->sin6_addr))
428            return false;
429        else {
430            struct in_addr in;
431
432            memcpy(&in, a6->sin6_addr.s6_addr + 12, sizeof(in));
433            return (in.s_addr == b4->sin_addr.s_addr);
434        }
435    }
436#endif
437
438    if (a->sa_family != AF_INET || b->sa_family != AF_INET)
439        return false;
440    return (a4->sin_addr.s_addr == b4->sin_addr.s_addr);
441}
442
443
444/*
445 * Returns the port of a sockaddr or 0 on error.
446 */
447unsigned short
448network_sockaddr_port(const struct sockaddr *sa)
449{
450    const struct sockaddr_in *sin;
451
452#ifdef HAVE_INET6
453    const struct sockaddr_in6 *sin6;
454
455    if (sa->sa_family == AF_INET6) {
456        sin6 = (const struct sockaddr_in6 *) sa;
457        return htons(sin6->sin6_port);
458    }
459#endif
460    if (sa->sa_family != AF_INET)
461        return 0;
462    else {
463        sin = (const struct sockaddr_in *) sa;
464        return htons(sin->sin_port);
465    }
466}
467
468
469/*
470 * Compare two addresses given as strings, applying an optional mask.  Returns
471 * true if the addresses are equal modulo the mask and false otherwise,
472 * including on syntax errors in the addresses or mask specification.
473 */
474bool
475network_addr_match(const char *a, const char *b, const char *mask)
476{
477    struct in_addr a4, b4, tmp;
478    unsigned long cidr;
479    char *end;
480    unsigned int i;
481    unsigned long bits, addr_mask;
482#ifdef HAVE_INET6
483    struct in6_addr a6, b6;
484#endif
485
486    /*
487     * If the addresses are IPv4, the mask may be in one of two forms.  It can
488     * either be a traditional mask, like 255.255.0.0, or it can be a CIDR
489     * subnet designation, like 16.  (The caller should have already removed
490     * the slash separating it from the address.)
491     */
492    if (inet_aton(a, &a4) && inet_aton(b, &b4)) {
493        if (mask == NULL)
494            addr_mask = htonl(0xffffffffUL);
495        else if (strchr(mask, '.') == NULL) {
496            cidr = strtoul(mask, &end, 10);
497            if (cidr > 32 || *end != '\0')
498                return false;
499            for (bits = 0, i = 0; i < cidr; i++)
500                bits |= (1 << (31 - i));
501            addr_mask = htonl(bits);
502        } else if (inet_aton(mask, &tmp))
503            addr_mask = tmp.s_addr;
504        else
505            return false;
506        return (a4.s_addr & addr_mask) == (b4.s_addr & addr_mask);
507    }
508           
509#ifdef HAVE_INET6
510    /*
511     * Otherwise, if the address is IPv6, the mask is required to be a CIDR
512     * subnet designation.
513     */
514    if (!inet_pton(AF_INET6, a, &a6) || !inet_pton(AF_INET6, b, &b6))
515        return false;
516    if (mask == NULL)
517        cidr = 128;
518    else {
519        cidr = strtoul(mask, &end, 10);
520        if (cidr > 128 || *end != '\0')
521            return false;
522    }
523    for (i = 0; i * 8 < cidr; i++) {
524        if ((i + 1) * 8 <= cidr) {
525            if (a6.s6_addr[i] != b6.s6_addr[i])
526                return false;
527        } else {
528            for (addr_mask = 0, bits = 0; bits < cidr % 8; bits++)
529                addr_mask |= (1 << (7 - bits));
530            if ((a6.s6_addr[i] & addr_mask) != (b6.s6_addr[i] & addr_mask))
531                return false;
532        }
533    }
534    return true;
535#else
536    return false;
537#endif
538}
Note: See TracBrowser for help on using the repository browser.