| /* Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "apr_arch_networkio.h" |
| #include "apr_network_io.h" |
| #include "apr_support.h" |
| #include "apr_portable.h" |
| #include "apr_arch_inherit.h" |
| |
| #ifdef HAVE_GETIFADDRS |
| #include <net/if.h> |
| #include <ifaddrs.h> |
| #endif |
| |
| #ifdef HAVE_STRUCT_IPMREQ |
| /* Only UDP and Raw Sockets can be used for Multicast */ |
| static apr_status_t mcast_check_type(apr_socket_t *sock) |
| { |
| int type; |
| apr_status_t rv; |
| |
| rv = apr_socket_type_get(sock, &type); |
| |
| if (rv != APR_SUCCESS) { |
| return rv; |
| } |
| else if (type == SOCK_DGRAM || type == SOCK_RAW) { |
| return APR_SUCCESS; |
| } |
| else { |
| return APR_ENOTIMPL; |
| } |
| } |
| |
| static void fill_mip_v4(struct ip_mreq *mip, apr_sockaddr_t *mcast, |
| apr_sockaddr_t *iface) |
| { |
| mip->imr_multiaddr = mcast->sa.sin.sin_addr; |
| if (iface == NULL) { |
| mip->imr_interface.s_addr = INADDR_ANY; |
| } |
| else { |
| mip->imr_interface = iface->sa.sin.sin_addr; |
| } |
| } |
| |
| /* This function is only interested in AF_INET6 sockets, so a noop |
| * "return 0" implementation for the !APR_HAVE_IPV6 build is |
| * sufficient. */ |
| static unsigned int find_if_index(const apr_sockaddr_t *iface) |
| { |
| unsigned int index = 0; |
| #if defined(HAVE_GETIFADDRS) && APR_HAVE_IPV6 |
| struct ifaddrs *ifp, *ifs; |
| |
| /** |
| * TODO: getifaddrs is only portable to *BSD and OS X. Using ioctl |
| * and SIOCGIFCONF is needed for Linux/Solaris support. |
| * |
| * There is a wrapper that takes the messy ioctl interface into |
| * getifaddrs. The license is acceptable, but, It is a fairly large |
| * chunk of code. |
| */ |
| if (getifaddrs(&ifs) != 0) { |
| return 0; |
| } |
| |
| for (ifp = ifs; ifp; ifp = ifp->ifa_next) { |
| if (ifp->ifa_addr != NULL && ifp->ifa_addr->sa_family == AF_INET6) { |
| if (memcmp(&iface->sa.sin6.sin6_addr, |
| &ifp->ifa_addr->sa_data[0], |
| sizeof(iface->sa.sin6.sin6_addr)) == 0) { |
| index = if_nametoindex(ifp->ifa_name); |
| break; |
| } |
| } |
| } |
| |
| freeifaddrs(ifs); |
| #endif |
| return index; |
| } |
| |
| #if APR_HAVE_IPV6 |
| static void fill_mip_v6(struct ipv6_mreq *mip, const apr_sockaddr_t *mcast, |
| const apr_sockaddr_t *iface) |
| { |
| memcpy(&mip->ipv6mr_multiaddr, mcast->ipaddr_ptr, |
| sizeof(mip->ipv6mr_multiaddr)); |
| |
| if (iface == NULL) { |
| mip->ipv6mr_interface = 0; |
| } |
| else { |
| mip->ipv6mr_interface = find_if_index(iface); |
| } |
| } |
| |
| #endif |
| |
| static int sock_is_ipv4(apr_socket_t *sock) |
| { |
| if (sock->local_addr->family == APR_INET) |
| return 1; |
| return 0; |
| } |
| |
| #if APR_HAVE_IPV6 |
| static int sock_is_ipv6(apr_socket_t *sock) |
| { |
| if (sock->local_addr->family == APR_INET6) |
| return 1; |
| return 0; |
| } |
| #endif |
| |
| static apr_status_t do_mcast(int type, apr_socket_t *sock, |
| apr_sockaddr_t *mcast, apr_sockaddr_t *iface, |
| apr_sockaddr_t *source) |
| { |
| struct ip_mreq mip4; |
| apr_status_t rv = APR_SUCCESS; |
| #if APR_HAVE_IPV6 |
| struct ipv6_mreq mip6; |
| #endif |
| #if MCAST_JOIN_SOURCE_GROUP |
| struct group_source_req mip; |
| int ip_proto; |
| #endif |
| |
| rv = mcast_check_type(sock); |
| |
| if (rv != APR_SUCCESS) { |
| return rv; |
| } |
| |
| if (source != NULL) { |
| #if MCAST_JOIN_SOURCE_GROUP |
| if (sock_is_ipv4(sock)) { |
| ip_proto = IPPROTO_IP; |
| } |
| #if APR_HAVE_IPV6 |
| else if (sock_is_ipv6(sock)) { |
| ip_proto = IPPROTO_IPV6; |
| } |
| #endif |
| else { |
| return APR_ENOTIMPL; |
| } |
| |
| if (type == IP_ADD_MEMBERSHIP) |
| type = MCAST_JOIN_SOURCE_GROUP; |
| else if (type == IP_DROP_MEMBERSHIP) |
| type = MCAST_LEAVE_SOURCE_GROUP; |
| else |
| return APR_ENOTIMPL; |
| |
| mip.gsr_interface = find_if_index(iface); |
| memcpy(&mip.gsr_group, mcast->ipaddr_ptr, sizeof(mip.gsr_group)); |
| memcpy(&mip.gsr_source, source->ipaddr_ptr, sizeof(mip.gsr_source)); |
| |
| if (setsockopt(sock->socketdes, ip_proto, type, (const void *) &mip, |
| sizeof(mip)) == -1) { |
| rv = errno; |
| } |
| #else |
| /* We do not support Source-Specific Multicast. */ |
| return APR_ENOTIMPL; |
| #endif |
| } |
| else { |
| if (sock_is_ipv4(sock)) { |
| |
| fill_mip_v4(&mip4, mcast, iface); |
| |
| if (setsockopt(sock->socketdes, IPPROTO_IP, type, |
| (const void *) &mip4, sizeof(mip4)) == -1) { |
| rv = errno; |
| } |
| } |
| #if APR_HAVE_IPV6 && defined(IPV6_JOIN_GROUP) && defined(IPV6_LEAVE_GROUP) |
| else if (sock_is_ipv6(sock)) { |
| if (type == IP_ADD_MEMBERSHIP) { |
| type = IPV6_JOIN_GROUP; |
| } |
| else if (type == IP_DROP_MEMBERSHIP) { |
| type = IPV6_LEAVE_GROUP; |
| } |
| else { |
| return APR_ENOTIMPL; |
| } |
| |
| fill_mip_v6(&mip6, mcast, iface); |
| |
| if (setsockopt(sock->socketdes, IPPROTO_IPV6, type, |
| (const void *) &mip6, sizeof(mip6)) == -1) { |
| rv = errno; |
| } |
| } |
| #endif |
| else { |
| rv = APR_ENOTIMPL; |
| } |
| } |
| return rv; |
| } |
| |
| static apr_status_t do_mcast_opt(int type, apr_socket_t *sock, |
| apr_byte_t value) |
| { |
| apr_status_t rv = APR_SUCCESS; |
| |
| rv = mcast_check_type(sock); |
| |
| if (rv != APR_SUCCESS) { |
| return rv; |
| } |
| |
| if (sock_is_ipv4(sock)) { |
| if (setsockopt(sock->socketdes, IPPROTO_IP, type, |
| (const void *) &value, sizeof(value)) == -1) { |
| rv = errno; |
| } |
| } |
| #if APR_HAVE_IPV6 |
| else if (sock_is_ipv6(sock) && type == IP_MULTICAST_LOOP) { |
| unsigned int loopopt = value; |
| type = IPV6_MULTICAST_LOOP; |
| if (setsockopt(sock->socketdes, IPPROTO_IPV6, type, |
| (const void *) &loopopt, sizeof(loopopt)) == -1) { |
| rv = errno; |
| } |
| } |
| else if (sock_is_ipv6(sock)) { |
| if (type == IP_MULTICAST_TTL) { |
| type = IPV6_MULTICAST_HOPS; |
| } |
| else { |
| return APR_ENOTIMPL; |
| } |
| |
| if (setsockopt(sock->socketdes, IPPROTO_IPV6, type, |
| &value, sizeof(value)) == -1) { |
| rv = errno; |
| } |
| } |
| #endif |
| else { |
| rv = APR_ENOTIMPL; |
| } |
| |
| return rv; |
| } |
| #endif |
| |
| APR_DECLARE(apr_status_t) apr_mcast_join(apr_socket_t *sock, |
| apr_sockaddr_t *join, |
| apr_sockaddr_t *iface, |
| apr_sockaddr_t *source) |
| { |
| #if defined(IP_ADD_MEMBERSHIP) && defined(HAVE_STRUCT_IPMREQ) |
| return do_mcast(IP_ADD_MEMBERSHIP, sock, join, iface, source); |
| #else |
| return APR_ENOTIMPL; |
| #endif |
| } |
| |
| APR_DECLARE(apr_status_t) apr_mcast_leave(apr_socket_t *sock, |
| apr_sockaddr_t *addr, |
| apr_sockaddr_t *iface, |
| apr_sockaddr_t *source) |
| { |
| #if defined(IP_DROP_MEMBERSHIP) && defined(HAVE_STRUCT_IPMREQ) |
| return do_mcast(IP_DROP_MEMBERSHIP, sock, addr, iface, source); |
| #else |
| return APR_ENOTIMPL; |
| #endif |
| } |
| |
| APR_DECLARE(apr_status_t) apr_mcast_hops(apr_socket_t *sock, apr_byte_t ttl) |
| { |
| #if defined(IP_MULTICAST_TTL) && defined(HAVE_STRUCT_IPMREQ) |
| return do_mcast_opt(IP_MULTICAST_TTL, sock, ttl); |
| #else |
| return APR_ENOTIMPL; |
| #endif |
| } |
| |
| APR_DECLARE(apr_status_t) apr_mcast_loopback(apr_socket_t *sock, |
| apr_byte_t opt) |
| { |
| #if defined(IP_MULTICAST_LOOP) && defined(HAVE_STRUCT_IPMREQ) |
| return do_mcast_opt(IP_MULTICAST_LOOP, sock, opt); |
| #else |
| return APR_ENOTIMPL; |
| #endif |
| } |
| |
| APR_DECLARE(apr_status_t) apr_mcast_interface(apr_socket_t *sock, |
| apr_sockaddr_t *iface) |
| { |
| #if defined(IP_MULTICAST_IF) && defined(HAVE_STRUCT_IPMREQ) |
| apr_status_t rv = APR_SUCCESS; |
| |
| if (sock_is_ipv4(sock)) { |
| if (setsockopt(sock->socketdes, IPPROTO_IP, IP_MULTICAST_IF, |
| (const void *) &iface->sa.sin.sin_addr, |
| sizeof(iface->sa.sin.sin_addr)) == -1) { |
| rv = errno; |
| } |
| } |
| #if APR_HAVE_IPV6 |
| else if (sock_is_ipv6(sock)) { |
| unsigned int idx = find_if_index(iface); |
| if (setsockopt(sock->socketdes, IPPROTO_IPV6, IPV6_MULTICAST_IF, |
| (const void *) &idx, sizeof(idx)) == -1) { |
| rv = errno; |
| } |
| } |
| #endif |
| else { |
| rv = APR_ENOTIMPL; |
| } |
| return rv; |
| #else |
| return APR_ENOTIMPL; |
| #endif |
| } |