diff options
Diffstat (limited to 'lib/sockunion.c')
-rw-r--r-- | lib/sockunion.c | 403 |
1 files changed, 212 insertions, 191 deletions
diff --git a/lib/sockunion.c b/lib/sockunion.c index deac292c..9084c27e 100644 --- a/lib/sockunion.c +++ b/lib/sockunion.c @@ -249,11 +249,36 @@ sockunion_init_new(sockunion su, sa_family_t family) if (family != AF_UNSPEC) sockunion_set_family(su, family) ; + else + confirm(AF_UNSPEC == 0) ; return su ; } ; /*------------------------------------------------------------------------------ + * Get the length of the address in the given sockunion. + * + * Returns zero if AF_UNSPEC or not any known address family. + */ +extern int +sockunion_get_len(sockunion su) +{ + switch (su->sa.sa_family) + { + case AF_INET: + return sizeof(struct sockaddr_in) ; + +#ifdef HAVE_IPV6 + case AF_INET6: + return sizeof(struct sockaddr_in6) ; +#endif + + default: + return 0 ; + } ; +} ; + +/*------------------------------------------------------------------------------ * From the given string, fill in the given sockunion. * * Returns: 0 => OK -- sockunion filled in @@ -357,20 +382,19 @@ sockunion_su2str (union sockunion *su, enum MTYPE type) * If have an IPv6 mapped IPv4 address, convert it to an IPv4 address. */ extern void -sockunion_unmap_ipv4 (union sockunion *su) +sockunion_unmap_ipv4 (sockunion su) { #ifdef HAVE_IPV6 - union sockunion sux ; - struct sockaddr_in* su_in = &sux.sin ; - - if ( (su->sa.sa_family == AF_INET6) - && IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr) ) + if ( (sockunion_family(su) == AF_INET6) + && IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr) ) { - sockunion_init_new(&sux, AF_INET) ; - memcpy (&su_in->sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4) ; - su_in->sin_port = su->sin6.sin6_port ; - memcpy (su, &sux, sizeof(sux)) ; - confirm(sizeof(*su) == sizeof(sux)) ; + union sockunion sux[1] ; + + sockunion_init_new(sux, AF_INET) ; + memcpy (&sux->sin.sin_addr, &su->sin6.sin6_addr.s6_addr[12], 4) ; + sux->sin.sin_port = su->sin6.sin6_port ; + memcpy (su, sux, sizeof(*sux)) ; + confirm(sizeof(*su) == sizeof(*sux)) ; } #endif /* HAVE_IPV6 */ } @@ -379,20 +403,19 @@ sockunion_unmap_ipv4 (union sockunion *su) * If have an IPv4 address, convert it to an IPv6 mapped IPv4 address. */ extern void -sockunion_map_ipv4 (union sockunion *su) +sockunion_map_ipv4 (sockunion su) { #ifdef HAVE_IPV6 - union sockunion sux ; - struct sockaddr_in6* su_in6 = &sux.sin6 ; - - if (su->sa.sa_family == AF_INET) + if (sockunion_family(su) == AF_INET) { - sockunion_init_new(&sux, AF_INET6) ; - memset (((char *)&su_in6->sin6_addr) + 10, 0xFF, 2) ; - memcpy (((char *)&su_in6->sin6_addr) + 12, &su->sin.sin_addr, 4) ; - su_in6->sin6_port = su->sin.sin_port ; - memcpy (su, &sux, sizeof(sux)) ; - confirm(sizeof(*su) == sizeof(sux)) ; + union sockunion sux[1] ; + + sockunion_init_new(sux, AF_INET6) ; + memset (&sux->sin6.sin6_addr.s6_addr[10], 0xFF, 2) ; + memcpy (&sux->sin6.sin6_addr.s6_addr[12], &su->sin.sin_addr, 4) ; + sux->sin6.sin6_port = su->sin.sin_port ; + memcpy (su, sux, sizeof(*sux)) ; + confirm(sizeof(*su) == sizeof(*sux)) ; } #endif /* HAVE_IPV6 */ } @@ -414,7 +437,7 @@ sockunion_map_ipv4 (union sockunion *su) * -1 -- error -- not one of the above * -2 -- error -- one of the above */ -int +extern int sockunion_accept (int sock_fd, union sockunion *su) { socklen_t len; @@ -443,23 +466,22 @@ sockunion_accept (int sock_fd, union sockunion *su) * Returns: -1 : failed -- see errno * otherwise : socket * - * Logs a LOG_WARNING message if fails. + * Logs a LOG_ERR message if fails. */ extern int -sockunion_socket(sa_family_t family, int type, int protocol) +sockunion_socket(sockunion su, int type, int protocol) { int sock_fd ; int err ; - sock_fd = socket(family, type, protocol); + sock_fd = socket(sockunion_family(su), type, protocol); if (sock_fd >= 0) return sock_fd ; err = errno ; - zlog (NULL, LOG_WARNING, - "Cannot make socket family=%d, type=%d, protocol=%d: %s", - (int)family, type, protocol, errtoa(err, 0).str) ; + zlog_err("Cannot make socket family=%d, type=%d, protocol=%d: %s", + (int)sockunion_family(su), type, protocol, errtoa(err, 0).str) ; errno = err ; return -1; } @@ -470,15 +492,15 @@ sockunion_socket(sa_family_t family, int type, int protocol) * Returns: -1 : failed -- see errno * otherwise : socket * - * Logs a LOG_WARNING message if fails. + * Logs a LOG_ERR message if fails. */ -int -sockunion_stream_socket (union sockunion *su) +extern int +sockunion_stream_socket (sockunion su) { if (su->sa.sa_family == 0) su->sa.sa_family = AF_INET_UNION; - return sockunion_socket (su->sa.sa_family, SOCK_STREAM, 0); + return sockunion_socket (su, SOCK_STREAM, 0); } /*------------------------------------------------------------------------------ @@ -541,25 +563,24 @@ sockunion_connect(int sock_fd, union sockunion* peer_su, unsigned short port, /*------------------------------------------------------------------------------ * Start listening on given socket * - * Returns: 0 : OK (so far so good) - * < 0 : failed -- see errno + * Returns: >= 0 : OK (so far so good) + * < 0 : failed -- see errno * - * Logs a LOG_WARNING message if fails. + * Logs a LOG_ERR message if fails. */ extern int sockunion_listen(int sock_fd, int backlog) { - int ret, err ; + int ret ; ret = listen(sock_fd, backlog) ; - if (ret == 0) - return 0 ; - - err = errno ; - zlog (NULL, LOG_WARNING, "cannot listen on socket %d: %s", - sock_fd, errtoa(err, 0).str) ; - errno = err ; + if (ret < 0) + { + int err = errno ; + zlog_err("cannot listen on socket %d: %s", sock_fd, errtoa(err, 0).str) ; + errno = err ; + } ; return ret ; } ; @@ -567,28 +588,72 @@ sockunion_listen(int sock_fd, int backlog) /*------------------------------------------------------------------------------ * Bind socket to address/port. * - * Sets the given port into the sockunion su. + * If the 'any' parameter is true, sets the given sockunion to INADDR_ANY or + * the *socket* address family equivalent. * - * If the 'any' parameter is NULL, set the address part of sockunion to - * INADDR_ANY or the family equivalent. Note that for IPv6 this does not - * affect the flow/scope in the su. + * Sets the given port into the sockunion su. * * For good measure, sets sin_len or family equivalent if required. * + * If not 'any', and the given su does not have the same address family as the + * socket, then attempts to convert the su to the same family as the socket, + * by mapping or unmapping IPv4. + * * Performs bind() and logs a LOG_WARNING message if fails. * * Returns: >= 0 => OK * < 0 => failed -- see errno */ -int -sockunion_bind (int sock_fd, union sockunion *su, unsigned short port, - void* any) +extern int +sockunion_bind(int sock_fd, sockunion su, unsigned short port, bool any) { int sa_len ; int ret ; + int sock_family ; - if (any == NULL) - sockunion_set_addr_any(su) ; + sock_family = sockunion_getsockfamily(sock_fd) ; + + if (any) + { + /* Create an "any" -- of same family as the socket */ + sockunion_init_new(su, sock_family) ; + sockunion_set_addr_any(su) ; + } + else + { + /* Want to bind to a specific address. + * + * We provide bind with an address which matches the address family of + * the *socket*. + * + * If the socket is AF_INET, address may be AF_INET, or an AF_INET6 + * *provided* it is an IPv4 mapped address. + * + * If the socket is AF_INET6, address may be AF_INET or AF_NET6, and + * will map any IPv4 address. + * + * If we don't HAVE_IPV6, or we don't recognise an address family, + * then do nothing and let bind() return some sort of error. + */ +#ifdef HAVE_IPV6 + if (sock_family != sockunion_family(su)) + { + switch (sock_family) + { + case AF_INET: + sockunion_unmap_ipv4(su) ; /* unmap if AF_INET6 mapped IPv4 */ + break ; + + case AF_INET6: + sockunion_map_ipv4(su) ; /* map if AF_INET */ + break ; + + default: + break ; + } ; + } ; +#endif + } ; sa_len = sockunion_set_port(su, port) ; @@ -596,7 +661,7 @@ sockunion_bind (int sock_fd, union sockunion *su, unsigned short port, if (ret < 0) { int err = errno ; - zlog (NULL, LOG_WARNING, "cannot bind to %s port %d socket %d: %s", + zlog_warn("cannot bind to %s port %d socket %d: %s", sutoa(su).str, port, sock_fd, errtoa(err, 0).str) ; errno = err ; } ; @@ -605,72 +670,11 @@ sockunion_bind (int sock_fd, union sockunion *su, unsigned short port, } /*------------------------------------------------------------------------------ - * Set socket SO_REUSEADDR option - * - * Returns: >= 0 => OK - * < 0 => failed -- see errno - * - * Logs a LOG_WARNING message if fails. - */ -int -sockopt_reuseaddr (int sock_fd) -{ - int ret; - int on = 1; - - ret = setsockopt (sock_fd, SOL_SOCKET, SO_REUSEADDR, (void *) &on, - sizeof (on)); - if (ret < 0) - { - int err = errno ; - zlog (NULL, LOG_WARNING, - "cannot set sockopt SO_REUSEADDR to socket %d: %s", sock_fd, - errtoa(err, 0).str) ; - errno = err ; - } ; - - return ret ; -} - -/*------------------------------------------------------------------------------ - * Set socket SO_REUSEPORT option -- if it is locally supported. - * - * Returns: >= 0 => OK - * < 0 => failed -- see errno - * - * Logs a LOG_WARNING message if fails. - */ -int -sockopt_reuseport (int sock_fd) -{ - int ret; - -#ifdef SO_REUSEPORT - int on = 1; - ret = setsockopt (sock_fd, SOL_SOCKET, SO_REUSEPORT, - (void *) &on, sizeof (on)); -#else - ret = 0 ; -#endif - - if (ret < 0) - { - int err = errno ; - zlog (NULL, LOG_WARNING, - "cannot set sockopt SO_REUSEPORT to socket %d: %s", sock_fd, - errtoa(err, 0).str) ; - errno = err ; - } ; - - return ret ; -} ; - -/*------------------------------------------------------------------------------ - * If same family and same prefix return 1. + * If same (known) family and same prefix return 1, otherwise return 0. * * Returns 0 if same family, but not a known family. */ -int +extern int sockunion_same (union sockunion *su1, union sockunion *su2) { int ret = 0; @@ -686,7 +690,7 @@ sockunion_same (union sockunion *su1, union sockunion *su2) #ifdef HAVE_IPV6 case AF_INET6: ret = memcmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr, - sizeof (struct in6_addr)); + sizeof (struct in6_addr)); return (ret == 0) ; #endif /* HAVE_IPV6 */ @@ -696,125 +700,142 @@ sockunion_same (union sockunion *su1, union sockunion *su2) } ; /*------------------------------------------------------------------------------ - * Get the address family the given socket is set to. + * Get local (getsockname) or remote (getpeername) address and port. * * Returns: >= 0 == the address family (AF_UNSPEC if fd sock_fd < 0) * < 0 => failed -- see errno * - * NB: gets the actual address family -- does NOT look for mapped IPv4. - */ -extern int -sockunion_getsockfamily(int sock_fd) -{ - union sockunion su ; - int ret ; - socklen_t len ; - - if (sock_fd < 0) - return AF_UNSPEC ; - - sockunion_init_new(&su, AF_UNSPEC) ; - len = sizeof(su) ; - - ret = getsockname(sock_fd, (struct sockaddr *)&su, &len) ; - if (ret < 0) - { - int err = errno ; - zlog_warn ("Failed in getsockname for socket %d: %s", - sock_fd, errtoa(err, 0).str) ; - errno = err ; - return -1 ; - } ; - - return su.sa.sa_family ; -} ; - -/*------------------------------------------------------------------------------ - * Get local or remote address and port. - * - * Returns: >= 0 => OK - * < 0 => failed (or unknown family) -- see errno - * - * If address is an IPv4 mapped IPv6 address, returns the IPv4 address. + * If "unmap": if address is an IPv4 mapped IPv6 address, returns AF_INET. * * NB: returns EAFNOSUPPORT if don't recognise the address family. + * + * Logs a LOG_ERR message if fails in getsockname/getpeername. */ static int -sockunion_get_name(int sock_fd, union sockunion* su, int local) +sockunion_get_name(int sock_fd, union sockunion* su, bool local, bool unmap) { int ret ; - + socklen_t len ; union { - struct sockaddr sa; - struct sockaddr_in sin; -#ifdef HAVE_IPV6 - struct sockaddr_in6 sin6; -#endif /* HAVE_IPV6 */ + union sockunion su ; char tmp_buffer[128]; } name ; - socklen_t len = sizeof(name) ; + memset(su, 0, sizeof(union sockunion)) ; + + confirm(AF_UNSPEC == 0) ; + if (sock_fd < 0) + return AF_UNSPEC ; + + len = sizeof(name) ; memset(&name, 0, len); - memset(su, 0, sizeof(union sockunion)) ; - if (local) - ret = getsockname(sock_fd, (struct sockaddr *)&name, &len) ; + if (local) + ret = getsockname(sock_fd, &name.su.sa, &len) ; else - ret = getpeername(sock_fd, (struct sockaddr *)&name, &len) ; + ret = getpeername(sock_fd, &name.su.sa, &len) ; if (ret < 0) { int err = errno ; - zlog_warn ("Cannot get %s address and port: %s", - local ? "local" : "remote", errtoa(err, 0).str) ; + zlog_err("failed in %s for socket %d: %s", + local ? "getsockname" : "getpeername", + sock_fd, errtoa(err, 0).str) ; errno = err ; - return ret ; } + else + { + ret = name.su.sa.sa_family ; - ret = 0 ; /* assume all will be well */ - switch (name.sa.sa_family) - { - case AF_INET: - memcpy(su, &name, sizeof (struct sockaddr_in)) ; - break ; + switch (ret) + { + case AF_INET: + su->sin = name.su.sin ; + break ; #ifdef HAVE_IPV6 - case AF_INET6: - memcpy(su, &name, sizeof (struct sockaddr_in6)) ; - sockunion_unmap_ipv4(su) ; - break ; + case AF_INET6: + su->sin6 = name.su.sin6 ; + if (unmap) + sockunion_unmap_ipv4(su) ; + break ; #endif /* HAVE_IPV6 */ - default: - errno = EAFNOSUPPORT ; - ret = -1 ; - } ; + default: + errno = EAFNOSUPPORT ; + ret = -1 ; + } ; + } ; return ret ; } ; /*------------------------------------------------------------------------------ - * Get local address and port -- ie getsockname(). + * Get the address family the given socket is set to. + * + * Returns: >= 0 == the address family (AF_UNSPEC if fd sock_fd < 0) + * < 0 => failed -- see errno + * + * NB: gets the actual address family -- does NOT look for mapped IPv4. + */ +extern int +sockunion_getsockfamily(int sock_fd) +{ + union sockunion su[1] ; + int ret ; + + ret = sockunion_get_name(sock_fd, su, true, /* true => local */ + false) ; /* false => don't unmap */ + return (ret >= 0) ? sockunion_family(su) : ret ; +} ; + +/*------------------------------------------------------------------------------ + * Get the address family the given socket's protocol is set to. + * + * If this is an AF_INET, that's easy. + * + * If this is an AF_INET6, then needs to look out for IN6_IS_ADDR_V4MAPPED. + * + * Returns: >= 0 == the address family (AF_UNSPEC if fd sock_fd < 0) + * < 0 => failed -- see errno + * + * NB: gets the underlying address family -- ie: looks for mapped IPv4. + */ +extern int +sockunion_getprotofamily(int sock_fd) +{ + union sockunion su[1] ; + int ret ; + + ret = sockunion_get_name(sock_fd, su, true, /* true => local */ + true) ; /* true => unmap */ + return (ret >= 0) ? sockunion_family(su) : ret ; +} ; + +/*------------------------------------------------------------------------------ + * Get local address and port -- ie getsockname(), except unmaps IPv4 mapped. * * See: sockunion_get_name() */ -int -sockunion_getsockname(int sock_fd, union sockunion* su_local) +extern int +sockunion_getsockname(int sock_fd, sockunion su_local) { - return sockunion_get_name(sock_fd, su_local, 1) ; + return sockunion_get_name(sock_fd, su_local, true, /* true => local */ + true) ; /* true => unmap */ } ; /*------------------------------------------------------------------------------ - * Get remote address and port -- ie getpeername(). + * Get remote address and port -- ie getpeername(), except unmaps IPv4 mapped. * * See: sockunion_get_name() */ -int -sockunion_getpeername (int sock_fd, union sockunion* su_remote) +extern int +sockunion_getpeername (int sock_fd, sockunion su_remote) { - return sockunion_get_name(sock_fd, su_remote, 0) ; + return sockunion_get_name(sock_fd, su_remote, false, /* false => remote */ + true) ; /* true => unmap */ } ; /*------------------------------------------------------------------------------ |