From: Sarah Day Date: Wed, 10 Feb 2016 19:48:44 +0000 (-0500) Subject: Refactor pktinfo code X-Git-Tag: krb5-1.15-beta1~269 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=076d6dbc50d683be540274654b35270fa8cfc289;p=thirdparty%2Fkrb5.git Refactor pktinfo code Move preprocessor conditionals outside of functions and simplify preprocessor statements in udppktinfo.c. --- diff --git a/src/lib/apputils/udppktinfo.c b/src/lib/apputils/udppktinfo.c index 383b474ecf..aa601faa19 100644 --- a/src/lib/apputils/udppktinfo.c +++ b/src/lib/apputils/udppktinfo.c @@ -28,9 +28,21 @@ #include #include +#if defined(IP_PKTINFO) && defined(HAVE_STRUCT_IN_PKTINFO) +#define HAVE_IP_PKTINFO +#endif + +#if defined(IPV6_PKTINFO) && defined(HAVE_STRUCT_IN6_PKTINFO) +#define HAVE_IPV6_PKTINFO +#endif + +#if defined(HAVE_IP_PKTINFO) || defined(HAVE_IPV6_PKTINFO) +#define HAVE_PKTINFO_SUPPORT +#endif + /* Use RFC 3542 API below, but fall back from IPV6_RECVPKTINFO to IPV6_PKTINFO * for RFC 2292 implementations. */ -#ifndef IPV6_RECVPKTINFO +#if !defined(IPV6_RECVPKTINFO) && defined(IPV6_PKTINFO) #define IPV6_RECVPKTINFO IPV6_PKTINFO #endif @@ -40,7 +52,7 @@ #endif /* IP_RECVPKTINFO */ #if defined(CMSG_SPACE) && defined(HAVE_STRUCT_CMSGHDR) && \ -(defined(IP_PKTINFO) || defined(IPV6_PKTINFO)) + defined(HAVE_PKTINFO_SUPPORT) union pktinfo { #ifdef HAVE_STRUCT_IN6_PKTINFO struct in6_pktinfo pi6; @@ -50,7 +62,62 @@ union pktinfo { #endif char c; }; -#endif +#endif /* HAVE_IPV6_PKTINFO && HAVE_STRUCT_CMSGHDR && HAVE_PKTINFO_SUPPORT */ + +/* + * Check if a socket is bound to a wildcard address. + * Returns 1 if it is, 0 if it's bound to a specific address, or -1 on error + * with errno set to the error. + */ +static int +is_socket_bound_to_wildcard(int sock) +{ + struct sockaddr_storage bound_addr; + socklen_t bound_addr_len = sizeof(bound_addr); + + if (getsockname(sock, ss2sa(&bound_addr), &bound_addr_len) < 0) + return -1; + + switch (ss2sa(&bound_addr)->sa_family) { + case AF_INET: + return ss2sin(&bound_addr)->sin_addr.s_addr == INADDR_ANY; + case AF_INET6: + return IN6_IS_ADDR_UNSPECIFIED(&ss2sin6(&bound_addr)->sin6_addr); + default: + errno = EINVAL; + return -1; + } +} + +#ifdef HAVE_IP_PKTINFO + +#define set_ipv4_pktinfo set_ipv4_recvpktinfo +static inline krb5_error_code +set_ipv4_recvpktinfo(int sock) +{ + int sockopt = 1; + return setsockopt(sock, IPPROTO_IP, IP_RECVPKTINFO, &sockopt, + sizeof(sockopt)); +} + +#else /* HAVE_IP_PKTINFO */ +#define set_ipv4_pktinfo(s) EINVAL +#endif /* HAVE_IP_PKTINFO */ + +#ifdef HAVE_IPV6_PKTINFO + +#define set_ipv6_pktinfo set_ipv6_recvpktinfo +static inline krb5_error_code +set_ipv6_recvpktinfo(int sock) +{ + int sockopt = 1; + return setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &sockopt, + sizeof(sockopt)); +} + +#else /* HAVE_IPV6_PKTINFO */ +#define set_ipv6_pktinfo(s) EINVAL +#endif /* HAVE_IPV6_PKTINFO */ /* * Set pktinfo option on a socket. Takes a socket and the socket address family @@ -62,30 +129,92 @@ union pktinfo { krb5_error_code set_pktinfo(int sock, int family) { - int sockopt = 1; - int option = 0, proto = 0; - switch (family) { -#if defined(IP_PKTINFO) && defined(HAVE_STRUCT_IN_PKTINFO) case AF_INET: - proto = IPPROTO_IP; - option = IP_RECVPKTINFO; - break; -#endif -#if defined(IPV6_PKTINFO) && defined(HAVE_STRUCT_IN6_PKTINFO) + return set_ipv4_pktinfo(sock); case AF_INET6: - proto = IPPROTO_IPV6; - option = IPV6_RECVPKTINFO; - break; -#endif + return set_ipv6_pktinfo(sock); default: return EINVAL; } - if (setsockopt(sock, proto, option, &sockopt, sizeof(sockopt))) - return errno; +} + +#if defined(HAVE_PKTINFO_SUPPORT) && defined(CMSG_SPACE) + +#ifdef HAVE_IP_PKTINFO + +static inline struct in_pktinfo * +cmsg2pktinfo(struct cmsghdr *cmsgptr) +{ + return (struct in_pktinfo *)(void *)CMSG_DATA(cmsgptr); +} + +#define check_cmsg_v4_pktinfo check_cmsg_ip_pktinfo +static int +check_cmsg_ip_pktinfo(struct cmsghdr *cmsgptr, struct sockaddr *to, + socklen_t *tolen, aux_addressing_info *auxaddr) +{ + struct in_pktinfo *pktinfo; + + if (cmsgptr->cmsg_level == IPPROTO_IP && + cmsgptr->cmsg_type == IP_PKTINFO && + *tolen >= sizeof(struct sockaddr_in)) { + + memset(to, 0, sizeof(struct sockaddr_in)); + pktinfo = cmsg2pktinfo(cmsgptr); + sa2sin(to)->sin_addr = pktinfo->ipi_addr; + sa2sin(to)->sin_family = AF_INET; + *tolen = sizeof(struct sockaddr_in); + return 1; + } return 0; } +#else /* HAVE_IP_PKTINFO */ +#define check_cmsg_v4_pktinfo(c, t, l, a) 0 +#endif /* HAVE_IP_PKTINFO */ + +#ifdef HAVE_IPV6_PKTINFO + +static inline struct in6_pktinfo * +cmsg2pktinfo6(struct cmsghdr *cmsgptr) +{ + return (struct in6_pktinfo *)(void *)CMSG_DATA(cmsgptr); +} + +#define check_cmsg_v6_pktinfo check_cmsg_ipv6_pktinfo +static int +check_cmsg_ipv6_pktinfo(struct cmsghdr *cmsgptr, struct sockaddr *to, + socklen_t *tolen, aux_addressing_info *auxaddr) +{ + struct in6_pktinfo *pktinfo; + + if (cmsgptr->cmsg_level == IPPROTO_IPV6 && + cmsgptr->cmsg_type == IPV6_PKTINFO && + *tolen >= sizeof(struct sockaddr_in6)) { + + memset(to, 0, sizeof(struct sockaddr_in6)); + pktinfo = cmsg2pktinfo6(cmsgptr); + sa2sin6(to)->sin6_addr = pktinfo->ipi6_addr; + sa2sin6(to)->sin6_family = AF_INET6; + *tolen = sizeof(struct sockaddr_in6); + auxaddr->ipv6_ifindex = pktinfo->ipi6_ifindex; + return 1; + } + return 0; +} +#else /* HAVE_IPV6_PKTINFO */ +#define check_cmsg_v6_pktinfo(c, t, l, a) 0 +#endif /* HAVE_IPV6_PKTINFO */ + +static int +check_cmsg_pktinfo(struct cmsghdr *cmsgptr, struct sockaddr *to, + socklen_t *tolen, aux_addressing_info *auxaddr) +{ + return check_cmsg_v4_pktinfo(cmsgptr, to, tolen, auxaddr) || + check_cmsg_v6_pktinfo(cmsgptr, to, tolen, auxaddr); +} + /* * Receive a message from a socket. * @@ -106,32 +235,27 @@ set_pktinfo(int sock, int family) */ krb5_error_code recv_from_to(int socket, void *buf, size_t len, int flags, - struct sockaddr *from, socklen_t *fromlen, - struct sockaddr *to, socklen_t *tolen, + struct sockaddr *from, socklen_t * fromlen, + struct sockaddr *to, socklen_t * tolen, aux_addressing_info *auxaddr) { -#if (!defined(IP_PKTINFO) && !defined(IPV6_PKTINFO)) || !defined(CMSG_SPACE) - if (to && tolen) { - /* Clobber with something recognizeable in case we try to use - * the address. */ - memset(to, 0x40, *tolen); - *tolen = 0; - } - - return recvfrom(socket, buf, len, flags, from, fromlen); -#else int r; struct iovec iov; char cmsg[CMSG_SPACE(sizeof(union pktinfo))]; struct cmsghdr *cmsgptr; struct msghdr msg; - if (!to || !tolen) + /* Don't use pktinfo if the socket isn't bound to a wildcard address. */ + r = is_socket_bound_to_wildcard(socket); + if (r < 0) + return errno; + + if (!to || !tolen || !r) return recvfrom(socket, buf, len, flags, from, fromlen); - /* Clobber with something recognizeable in case we can't extract - * the address but try to use it anyways. */ + /* Clobber with something recognizeable in case we can't extract the + * address but try to use it anyways. */ memset(to, 0x40, *tolen); iov.iov_base = buf; @@ -149,47 +273,102 @@ recv_from_to(int socket, void *buf, size_t len, int flags, return r; *fromlen = msg.msg_namelen; - /* On Darwin (and presumably all *BSD with KAME stacks), - * CMSG_FIRSTHDR doesn't check for a non-zero controllen. RFC - * 3542 recommends making this check, even though the (new) spec - * for CMSG_FIRSTHDR says it's supposed to do the check. */ + /* + * On Darwin (and presumably all *BSD with KAME stacks), CMSG_FIRSTHDR + * doesn't check for a non-zero controllen. RFC 3542 recommends making + * this check, even though the (new) spec for CMSG_FIRSTHDR says it's + * supposed to do the check. + */ if (msg.msg_controllen) { cmsgptr = CMSG_FIRSTHDR(&msg); while (cmsgptr) { -#ifdef IP_PKTINFO - if (cmsgptr->cmsg_level == IPPROTO_IP - && cmsgptr->cmsg_type == IP_PKTINFO - && *tolen >= sizeof(struct sockaddr_in)) { - struct in_pktinfo *pktinfo; - memset(to, 0, sizeof(struct sockaddr_in)); - pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsgptr); - ((struct sockaddr_in *)to)->sin_addr = pktinfo->ipi_addr; - ((struct sockaddr_in *)to)->sin_family = AF_INET; - *tolen = sizeof(struct sockaddr_in); - return r; - } -#endif -#if defined(IPV6_PKTINFO) && defined(HAVE_STRUCT_IN6_PKTINFO) - if (cmsgptr->cmsg_level == IPPROTO_IPV6 - && cmsgptr->cmsg_type == IPV6_PKTINFO - && *tolen >= sizeof(struct sockaddr_in6)) { - struct in6_pktinfo *pktinfo; - memset(to, 0, sizeof(struct sockaddr_in6)); - pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsgptr); - ((struct sockaddr_in6 *)to)->sin6_addr = pktinfo->ipi6_addr; - ((struct sockaddr_in6 *)to)->sin6_family = AF_INET6; - *tolen = sizeof(struct sockaddr_in6); - auxaddr->ipv6_ifindex = pktinfo->ipi6_ifindex; + if (check_cmsg_pktinfo(cmsgptr, to, tolen, auxaddr)) return r; - } -#endif cmsgptr = CMSG_NXTHDR(&msg, cmsgptr); } } /* No info about destination addr was available. */ *tolen = 0; return r; -#endif +} + +#ifdef HAVE_IP_PKTINFO + +#define set_msg_from_ipv4 set_msg_from_ip_pktinfo +static krb5_error_code +set_msg_from_ip_pktinfo(struct msghdr *msg, struct cmsghdr *cmsgptr, + struct sockaddr *from, socklen_t fromlen, + aux_addressing_info *auxaddr) +{ + struct in_pktinfo *p = cmsg2pktinfo(cmsgptr); + const struct sockaddr_in *from4 = sa2sin(from); + + if (fromlen != sizeof(struct sockaddr_in)) + return EINVAL; + cmsgptr->cmsg_level = IPPROTO_IP; + cmsgptr->cmsg_type = IP_PKTINFO; + cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + p->ipi_spec_dst = from4->sin_addr; + + msg->msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); + return 0; +} + +#else /* HAVE_IP_PKTINFO */ +#define set_msg_from_ipv4(m, c, f, l, a) EINVAL +#endif /* HAVE_IP_PKTINFO */ + +#ifdef HAVE_IPV6_PKTINFO + +#define set_msg_from_ipv6 set_msg_from_ipv6_pktinfo +static krb5_error_code +set_msg_from_ipv6_pktinfo(struct msghdr *msg, struct cmsghdr *cmsgptr, + struct sockaddr *from, socklen_t fromlen, + aux_addressing_info *auxaddr) +{ + struct in6_pktinfo *p = cmsg2pktinfo6(cmsgptr); + const struct sockaddr_in6 *from6 = sa2sin6(from); + + if (fromlen != sizeof(struct sockaddr_in6)) + return EINVAL; + cmsgptr->cmsg_level = IPPROTO_IPV6; + cmsgptr->cmsg_type = IPV6_PKTINFO; + cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + + p->ipi6_addr = from6->sin6_addr; + /* + * Because of the possibility of asymmetric routing, we + * normally don't want to specify an interface. However, + * Mac OS X doesn't like sending from a link-local address + * (which can come up in testing at least, if you wind up + * with a "foo.local" name) unless we do specify the + * interface. + */ + if (IN6_IS_ADDR_LINKLOCAL(&from6->sin6_addr)) + p->ipi6_ifindex = auxaddr->ipv6_ifindex; + /* otherwise, already zero */ + + msg->msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); + return 0; +} + +#else /* HAVE_IPV6_PKTINFO */ +#define set_msg_from_ipv6(m, c, f, l, a) EINVAL +#endif /* HAVE_IPV6_PKTINFO */ + +static krb5_error_code +set_msg_from(int family, struct msghdr *msg, struct cmsghdr *cmsgptr, + struct sockaddr *from, socklen_t fromlen, + aux_addressing_info *auxaddr) +{ + switch (family) { + case AF_INET: + return set_msg_from_ipv4(msg, cmsgptr, from, fromlen, auxaddr); + case AF_INET6: + return set_msg_from_ipv6(msg, cmsgptr, from, fromlen, auxaddr); + } + + return EINVAL; } /* @@ -210,22 +389,22 @@ recv_from_to(int socket, void *buf, size_t len, int flags, */ krb5_error_code send_to_from(int socket, void *buf, size_t len, int flags, - const struct sockaddr *to, socklen_t tolen, - const struct sockaddr *from, socklen_t fromlen, - aux_addressing_info *auxaddr) + const struct sockaddr *to, socklen_t tolen, struct sockaddr *from, + socklen_t fromlen, aux_addressing_info *auxaddr) { -#if (!defined(IP_PKTINFO) && !defined(IPV6_PKTINFO)) || !defined(CMSG_SPACE) - return sendto(socket, buf, len, flags, to, tolen); -#else + int r; struct iovec iov; struct msghdr msg; struct cmsghdr *cmsgptr; char cbuf[CMSG_SPACE(sizeof(union pktinfo))]; - if (from == 0 || fromlen == 0 || from->sa_family != to->sa_family) { -use_sendto: - return sendto(socket, buf, len, flags, to, tolen); - } + /* Don't use pktinfo if the socket isn't bound to a wildcard address. */ + r = is_socket_bound_to_wildcard(socket); + if (r < 0) + return errno; + + if (from == NULL || fromlen == 0 || from->sa_family != to->sa_family || !r) + goto use_sendto; iov.iov_base = buf; iov.iov_len = len; @@ -239,58 +418,45 @@ use_sendto: msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = cbuf; - /* CMSG_FIRSTHDR needs a non-zero controllen, or it'll return NULL - * on Linux. */ + /* CMSG_FIRSTHDR needs a non-zero controllen, or it'll return NULL on + * Linux. */ msg.msg_controllen = sizeof(cbuf); cmsgptr = CMSG_FIRSTHDR(&msg); msg.msg_controllen = 0; - switch (from->sa_family) { -#if defined(IP_PKTINFO) - case AF_INET: - if (fromlen != sizeof(struct sockaddr_in)) - goto use_sendto; - cmsgptr->cmsg_level = IPPROTO_IP; - cmsgptr->cmsg_type = IP_PKTINFO; - cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); - { - struct in_pktinfo *p = (struct in_pktinfo *)CMSG_DATA(cmsgptr); - const struct sockaddr_in *from4 = (const struct sockaddr_in *)from; - p->ipi_spec_dst = from4->sin_addr; - } - msg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); - break; -#endif -#if defined(IPV6_PKTINFO) && defined(HAVE_STRUCT_IN6_PKTINFO) - case AF_INET6: - if (fromlen != sizeof(struct sockaddr_in6)) - goto use_sendto; - cmsgptr->cmsg_level = IPPROTO_IPV6; - cmsgptr->cmsg_type = IPV6_PKTINFO; - cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); - { - struct in6_pktinfo *p = (struct in6_pktinfo *)CMSG_DATA(cmsgptr); - const struct sockaddr_in6 *from6 = - (const struct sockaddr_in6 *)from; - p->ipi6_addr = from6->sin6_addr; - /* - * Because of the possibility of asymmetric routing, we - * normally don't want to specify an interface. However, - * Mac OS X doesn't like sending from a link-local address - * (which can come up in testing at least, if you wind up - * with a "foo.local" name) unless we do specify the - * interface. - */ - if (IN6_IS_ADDR_LINKLOCAL(&from6->sin6_addr)) - p->ipi6_ifindex = auxaddr->ipv6_ifindex; - /* otherwise, already zero */ - } - msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); - break; -#endif - default: + if (set_msg_from(from->sa_family, &msg, cmsgptr, from, fromlen, auxaddr)) goto use_sendto; - } return sendmsg(socket, &msg, flags); -#endif + +use_sendto: + return sendto(socket, buf, len, flags, to, tolen); } + +#else /* HAVE_PKTINFO_SUPPORT && CMSG_SPACE */ + +krb5_error_code +recv_from_to(int socket, void *buf, size_t len, int flags, + struct sockaddr *from, socklen_t *fromlen, + struct sockaddr *to, socklen_t *tolen, + aux_addressing_info *auxaddr) +{ + if (to && tolen) { + /* Clobber with something recognizeable in case we try to use the + * address. */ + memset(to, 0x40, *tolen); + *tolen = 0; + } + + return recvfrom(socket, buf, len, flags, from, fromlen); +} + +krb5_error_code +send_to_from(int socket, void *buf, size_t len, int flags, + const struct sockaddr *to, socklen_t tolen, + const struct sockaddr *from, socklen_t fromlen, + aux_addressing_info *auxaddr) +{ + return sendto(socket, buf, len, flags, to, tolen); +} + +#endif /* HAVE_PKTINFO_SUPPORT && CMSG_SPACE */ diff --git a/src/lib/apputils/udppktinfo.h b/src/lib/apputils/udppktinfo.h index af3702868c..53a584a82b 100644 --- a/src/lib/apputils/udppktinfo.h +++ b/src/lib/apputils/udppktinfo.h @@ -52,8 +52,7 @@ recv_from_to(int socket, void *buf, size_t len, int flags, krb5_error_code send_to_from(int socket, void *buf, size_t len, int flags, - const struct sockaddr *to, socklen_t tolen, - const struct sockaddr *from, socklen_t fromlen, - aux_addressing_info *auxaddr); + const struct sockaddr *to, socklen_t tolen, struct sockaddr *from, + socklen_t fromlen, aux_addressing_info *auxaddr); #endif /* UDPPKTINFO_H */