1251875Speter/* Licensed to the Apache Software Foundation (ASF) under one or more
2251875Speter * contributor license agreements.  See the NOTICE file distributed with
3251875Speter * this work for additional information regarding copyright ownership.
4251875Speter * The ASF licenses this file to You under the Apache License, Version 2.0
5251875Speter * (the "License"); you may not use this file except in compliance with
6251875Speter * the License.  You may obtain a copy of the License at
7251875Speter *
8251875Speter *     http://www.apache.org/licenses/LICENSE-2.0
9251875Speter *
10251875Speter * Unless required by applicable law or agreed to in writing, software
11251875Speter * distributed under the License is distributed on an "AS IS" BASIS,
12251875Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13251875Speter * See the License for the specific language governing permissions and
14251875Speter * limitations under the License.
15251875Speter */
16251875Speter
17251875Speter#include "apr_arch_networkio.h"
18251875Speter#include "apr_network_io.h"
19251875Speter#include "apr_support.h"
20251875Speter#include "apr_portable.h"
21251875Speter#include "apr_arch_inherit.h"
22251875Speter
23251875Speter#ifdef HAVE_GETIFADDRS
24251875Speter#include <net/if.h>
25251875Speter#include <ifaddrs.h>
26251875Speter#endif
27251875Speter
28251875Speter#ifdef HAVE_STRUCT_IPMREQ
29251875Speterstatic void fill_mip_v4(struct ip_mreq *mip, apr_sockaddr_t *mcast,
30251875Speter                        apr_sockaddr_t *iface)
31251875Speter{
32251875Speter    mip->imr_multiaddr = mcast->sa.sin.sin_addr;
33251875Speter    if (iface == NULL) {
34251875Speter        mip->imr_interface.s_addr = INADDR_ANY;
35251875Speter    }
36251875Speter    else {
37251875Speter        mip->imr_interface = iface->sa.sin.sin_addr;
38251875Speter    }
39251875Speter}
40251875Speter
41251875Speter/* This function is only interested in AF_INET6 sockets, so a noop
42251875Speter * "return 0" implementation for the !APR_HAVE_IPV6 build is
43251875Speter * sufficient. */
44251875Speterstatic unsigned int find_if_index(const apr_sockaddr_t *iface)
45251875Speter{
46251875Speter    unsigned int index = 0;
47251875Speter#if defined(HAVE_GETIFADDRS) && APR_HAVE_IPV6
48251875Speter    struct ifaddrs *ifp, *ifs;
49251875Speter
50251875Speter    /**
51251875Speter     * TODO: getifaddrs is only portable to *BSD and OS X. Using ioctl
52251875Speter     *       and SIOCGIFCONF is needed for Linux/Solaris support.
53251875Speter     *
54251875Speter     *       There is a wrapper that takes the messy ioctl interface into
55251875Speter     *       getifaddrs. The license is acceptable, but, It is a fairly large
56251875Speter     *       chunk of code.
57251875Speter     */
58251875Speter    if (getifaddrs(&ifs) != 0) {
59251875Speter        return 0;
60251875Speter    }
61251875Speter
62251875Speter    for (ifp = ifs; ifp; ifp = ifp->ifa_next) {
63251875Speter        if (ifp->ifa_addr != NULL && ifp->ifa_addr->sa_family == AF_INET6) {
64251875Speter            if (memcmp(&iface->sa.sin6.sin6_addr,
65251875Speter                       &ifp->ifa_addr->sa_data[0],
66251875Speter                       sizeof(iface->sa.sin6.sin6_addr)) == 0) {
67251875Speter                index = if_nametoindex(ifp->ifa_name);
68251875Speter                break;
69251875Speter            }
70251875Speter        }
71251875Speter    }
72251875Speter
73251875Speter    freeifaddrs(ifs);
74251875Speter#endif
75251875Speter    return index;
76251875Speter}
77251875Speter
78251875Speter#if APR_HAVE_IPV6
79251875Speterstatic void fill_mip_v6(struct ipv6_mreq *mip, const apr_sockaddr_t *mcast,
80251875Speter                        const apr_sockaddr_t *iface)
81251875Speter{
82251875Speter    memcpy(&mip->ipv6mr_multiaddr, mcast->ipaddr_ptr,
83251875Speter           sizeof(mip->ipv6mr_multiaddr));
84251875Speter
85251875Speter    if (iface == NULL) {
86251875Speter        mip->ipv6mr_interface = 0;
87251875Speter    }
88251875Speter    else {
89251875Speter        mip->ipv6mr_interface = find_if_index(iface);
90251875Speter    }
91251875Speter}
92251875Speter
93251875Speter#endif
94251875Speter
95251875Speterstatic int sock_is_ipv4(apr_socket_t *sock)
96251875Speter{
97251875Speter    if (sock->local_addr->family == APR_INET)
98251875Speter        return 1;
99251875Speter    return 0;
100251875Speter}
101251875Speter
102251875Speter#if APR_HAVE_IPV6
103251875Speterstatic int sock_is_ipv6(apr_socket_t *sock)
104251875Speter{
105251875Speter    if (sock->local_addr->family == APR_INET6)
106251875Speter        return 1;
107251875Speter    return 0;
108251875Speter}
109251875Speter#endif
110251875Speter
111251875Speterstatic apr_status_t do_mcast(int type, apr_socket_t *sock,
112251875Speter                             apr_sockaddr_t *mcast, apr_sockaddr_t *iface,
113251875Speter                             apr_sockaddr_t *source)
114251875Speter{
115251875Speter    struct ip_mreq mip4;
116251875Speter    apr_status_t rv = APR_SUCCESS;
117251875Speter#if APR_HAVE_IPV6
118251875Speter    struct ipv6_mreq mip6;
119251875Speter#endif
120251875Speter#ifdef GROUP_FILTER_SIZE
121251875Speter    struct group_source_req mip;
122251875Speter    int ip_proto;
123251875Speter#endif
124251875Speter
125251875Speter    if (source != NULL) {
126251875Speter#ifdef GROUP_FILTER_SIZE
127251875Speter        if (sock_is_ipv4(sock)) {
128251875Speter            ip_proto = IPPROTO_IP;
129251875Speter        }
130251875Speter#if APR_HAVE_IPV6
131251875Speter        else if (sock_is_ipv6(sock)) {
132251875Speter            ip_proto = IPPROTO_IPV6;
133251875Speter        }
134251875Speter#endif
135251875Speter        else {
136251875Speter            return APR_ENOTIMPL;
137251875Speter        }
138251875Speter
139251875Speter        if (type == IP_ADD_MEMBERSHIP)
140251875Speter            type = MCAST_JOIN_SOURCE_GROUP;
141251875Speter        else if (type == IP_DROP_MEMBERSHIP)
142251875Speter            type = MCAST_LEAVE_SOURCE_GROUP;
143251875Speter        else
144251875Speter            return APR_ENOTIMPL;
145251875Speter
146251875Speter        mip.gsr_interface = find_if_index(iface);
147251875Speter        memcpy(&mip.gsr_group, mcast->ipaddr_ptr, sizeof(mip.gsr_group));
148251875Speter        memcpy(&mip.gsr_source, source->ipaddr_ptr, sizeof(mip.gsr_source));
149251875Speter
150251875Speter        if (setsockopt(sock->socketdes, ip_proto, type, (const void *) &mip,
151251875Speter                       sizeof(mip)) == -1) {
152251875Speter            rv = errno;
153251875Speter        }
154251875Speter#else
155251875Speter        /* We do not support Source-Specific Multicast. */
156251875Speter        return APR_ENOTIMPL;
157251875Speter#endif
158251875Speter    }
159251875Speter    else {
160251875Speter        if (sock_is_ipv4(sock)) {
161251875Speter
162251875Speter            fill_mip_v4(&mip4, mcast, iface);
163251875Speter
164251875Speter            if (setsockopt(sock->socketdes, IPPROTO_IP, type,
165251875Speter                           (const void *) &mip4, sizeof(mip4)) == -1) {
166251875Speter                rv = errno;
167251875Speter            }
168251875Speter        }
169251875Speter#if APR_HAVE_IPV6 && defined(IPV6_JOIN_GROUP) && defined(IPV6_LEAVE_GROUP)
170251875Speter        else if (sock_is_ipv6(sock)) {
171251875Speter            if (type == IP_ADD_MEMBERSHIP) {
172251875Speter                type = IPV6_JOIN_GROUP;
173251875Speter            }
174251875Speter            else if (type == IP_DROP_MEMBERSHIP) {
175251875Speter                type = IPV6_LEAVE_GROUP;
176251875Speter            }
177251875Speter            else {
178251875Speter                return APR_ENOTIMPL;
179251875Speter            }
180251875Speter
181251875Speter            fill_mip_v6(&mip6, mcast, iface);
182251875Speter
183251875Speter            if (setsockopt(sock->socketdes, IPPROTO_IPV6, type,
184251875Speter                           (const void *) &mip6, sizeof(mip6)) == -1) {
185251875Speter                rv = errno;
186251875Speter            }
187251875Speter        }
188251875Speter#endif
189251875Speter        else {
190251875Speter            rv = APR_ENOTIMPL;
191251875Speter        }
192251875Speter    }
193251875Speter    return rv;
194251875Speter}
195251875Speter
196253734Speter/* Set the IP_MULTICAST_TTL or IP_MULTICAST_LOOP option, or IPv6
197253734Speter * equivalents, for the socket, to the given value.  Note that this
198253734Speter * function *only works* for those particular option types. */
199251875Speterstatic apr_status_t do_mcast_opt(int type, apr_socket_t *sock,
200251875Speter                                 apr_byte_t value)
201251875Speter{
202251875Speter    apr_status_t rv = APR_SUCCESS;
203251875Speter
204251875Speter    if (sock_is_ipv4(sock)) {
205253734Speter        /* For the IP_MULTICAST_* options, this must be a (char *)
206253734Speter         * pointer. */
207251875Speter        if (setsockopt(sock->socketdes, IPPROTO_IP, type,
208251875Speter                       (const void *) &value, sizeof(value)) == -1) {
209251875Speter            rv = errno;
210251875Speter        }
211251875Speter    }
212251875Speter#if APR_HAVE_IPV6
213251875Speter    else if (sock_is_ipv6(sock)) {
214253734Speter        /* For the IPV6_* options, an (int *) pointer must be used. */
215253734Speter        int ivalue = value;
216253734Speter
217251875Speter        if (type == IP_MULTICAST_TTL) {
218251875Speter            type = IPV6_MULTICAST_HOPS;
219251875Speter        }
220253734Speter        else if (type == IP_MULTICAST_LOOP) {
221253734Speter            type = IPV6_MULTICAST_LOOP;
222253734Speter        }
223251875Speter        else {
224251875Speter            return APR_ENOTIMPL;
225251875Speter        }
226251875Speter
227251875Speter        if (setsockopt(sock->socketdes, IPPROTO_IPV6, type,
228253734Speter                       (const void *) &ivalue, sizeof(ivalue)) == -1) {
229251875Speter            rv = errno;
230251875Speter        }
231251875Speter    }
232251875Speter#endif
233251875Speter    else {
234251875Speter        rv = APR_ENOTIMPL;
235251875Speter    }
236251875Speter
237251875Speter    return rv;
238251875Speter}
239251875Speter#endif
240251875Speter
241251875SpeterAPR_DECLARE(apr_status_t) apr_mcast_join(apr_socket_t *sock,
242251875Speter                                         apr_sockaddr_t *join,
243251875Speter                                         apr_sockaddr_t *iface,
244251875Speter                                         apr_sockaddr_t *source)
245251875Speter{
246251875Speter#if defined(IP_ADD_MEMBERSHIP) && defined(HAVE_STRUCT_IPMREQ)
247251875Speter    return do_mcast(IP_ADD_MEMBERSHIP, sock, join, iface, source);
248251875Speter#else
249251875Speter    return APR_ENOTIMPL;
250251875Speter#endif
251251875Speter}
252251875Speter
253251875SpeterAPR_DECLARE(apr_status_t) apr_mcast_leave(apr_socket_t *sock,
254251875Speter                                          apr_sockaddr_t *addr,
255251875Speter                                          apr_sockaddr_t *iface,
256251875Speter                                          apr_sockaddr_t *source)
257251875Speter{
258251875Speter#if defined(IP_DROP_MEMBERSHIP) && defined(HAVE_STRUCT_IPMREQ)
259251875Speter    return do_mcast(IP_DROP_MEMBERSHIP, sock, addr, iface, source);
260251875Speter#else
261251875Speter    return APR_ENOTIMPL;
262251875Speter#endif
263251875Speter}
264251875Speter
265251875SpeterAPR_DECLARE(apr_status_t) apr_mcast_hops(apr_socket_t *sock, apr_byte_t ttl)
266251875Speter{
267251875Speter#if defined(IP_MULTICAST_TTL) && defined(HAVE_STRUCT_IPMREQ)
268251875Speter    return do_mcast_opt(IP_MULTICAST_TTL, sock, ttl);
269251875Speter#else
270251875Speter    return APR_ENOTIMPL;
271251875Speter#endif
272251875Speter}
273251875Speter
274251875SpeterAPR_DECLARE(apr_status_t) apr_mcast_loopback(apr_socket_t *sock,
275251875Speter                                             apr_byte_t opt)
276251875Speter{
277251875Speter#if defined(IP_MULTICAST_LOOP) && defined(HAVE_STRUCT_IPMREQ)
278251875Speter    return do_mcast_opt(IP_MULTICAST_LOOP, sock, opt);
279251875Speter#else
280251875Speter    return APR_ENOTIMPL;
281251875Speter#endif
282251875Speter}
283251875Speter
284251875SpeterAPR_DECLARE(apr_status_t) apr_mcast_interface(apr_socket_t *sock,
285251875Speter                                              apr_sockaddr_t *iface)
286251875Speter{
287251875Speter#if defined(IP_MULTICAST_IF) && defined(HAVE_STRUCT_IPMREQ)
288251875Speter    apr_status_t rv = APR_SUCCESS;
289251875Speter
290251875Speter    if (sock_is_ipv4(sock)) {
291251875Speter        if (setsockopt(sock->socketdes, IPPROTO_IP, IP_MULTICAST_IF,
292251875Speter                       (const void *) &iface->sa.sin.sin_addr,
293251875Speter                       sizeof(iface->sa.sin.sin_addr)) == -1) {
294251875Speter            rv = errno;
295251875Speter        }
296251875Speter    }
297251875Speter#if APR_HAVE_IPV6
298251875Speter    else if (sock_is_ipv6(sock)) {
299251875Speter        unsigned int idx = find_if_index(iface);
300251875Speter        if (setsockopt(sock->socketdes, IPPROTO_IPV6, IPV6_MULTICAST_IF,
301251875Speter                       (const void *) &idx, sizeof(idx)) == -1) {
302251875Speter            rv = errno;
303251875Speter        }
304251875Speter    }
305251875Speter#endif
306251875Speter    else {
307251875Speter        rv = APR_ENOTIMPL;
308251875Speter    }
309251875Speter    return rv;
310251875Speter#else
311251875Speter    return APR_ENOTIMPL;
312251875Speter#endif
313251875Speter}
314