1/*
2 * $Id: mnc_multicast.c,v 1.8 2004/09/22 19:14:23 colmmacc Exp $
3 *
4 * mnc_multicast.c -- Multicast NetCat
5 *
6 * Colm MacCarthaigh, <colm@apache.org>
7 *
8 * copyright (c) 2007, Colm MacCarthaigh.
9 * Copyright (c) 2004 - 2006, HEAnet Ltd.
10 *
11 * This software is an open source.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 *
17 * Redistributions of source code must retain the above copyright notice,
18 * this list of conditions and the following disclaimer.
19 *
20 * Redistributions in binary form must reproduce the above copyright notice,
21 * this list of conditions and the following disclaimer in the documentation
22 * and/or other materials provided with the distribution.
23 *
24 * Neither the name of the HEAnet Ltd. nor the names of its contributors may
25 * be used to endorse or promote products derived from this software without
26 * specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
32 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38 * POSSIBILITY OF SUCH DAMAGE.
39 *
40 */
41
42#ifndef WINDOWS
43
44#include <sys/types.h>
45#include <sys/socket.h>
46#include <netinet/in.h>
47#include <arpa/inet.h>
48#include <net/if.h>
49#include <string.h>
50#include <netdb.h>
51#include <errno.h>
52
53#else
54
55#include <sys/types.h>
56#include <winsock2.h>
57#include <ws2tcpip.h>
58#include <stdlib.h>
59
60#endif
61
62#include "mnc.h"
63
64#ifndef MCAST_JOIN_GROUP
65
66#ifdef IP_ADD_SOURCE_MEMBERSHIP
67int mnc_join_ipv4_ssm(int socket, struct addrinfo * group,
68                      struct addrinfo * source, char * iface)
69{
70	struct	ip_mreq_source	multicast_request;
71
72	if (iface != NULL)
73	{
74		/* See if interface is a literal IPv4 address */
75		if ((multicast_request.imr_interface.s_addr =
76		     inet_addr(iface)) == INADDR_NONE)
77		{
78			mnc_warning("Invalid interface address\n");
79			return -1;
80		}
81	}
82	else
83	{
84		/* set the interface to the default */
85		multicast_request.imr_interface.s_addr = htonl(INADDR_ANY);
86	}
87
88	multicast_request.imr_multiaddr.s_addr =
89	                ((struct sockaddr_in *)group->ai_addr)->sin_addr.s_addr;
90
91	multicast_request.imr_sourceaddr.s_addr =
92	               ((struct sockaddr_in *)source->ai_addr)->sin_addr.s_addr;
93
94	/* Set the socket option */
95	if (setsockopt(socket, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP,
96	               (char *) &multicast_request,
97	               sizeof(multicast_request)) != 0)
98	{
99		mnc_warning("Could not join the multicast group: %s\n",
100		            strerror(errno));
101
102		return -1;
103	}
104
105	return 0;
106}
107#else
108
109int mnc_join_ipv4_ssm(int socket, struct addrinfo * group,
110                      struct addrinfo * source, char * iface)
111{
112	mnc_warning("Sorry, No support for IPv4 source-specific multicast in this build\n");
113
114	return -1;
115}
116#endif
117
118int mnc_join_ipv6_ssm(int socket, struct addrinfo * group,
119                      struct addrinfo * source, char * iface)
120{
121	mnc_warning("Sorry, No support for IPv6 source-specific multicast in this build\n");
122
123	return -1;
124}
125#else /* if MCAST_JOIN_GROUP  .. */
126
127#define mnc_join_ipv6_asm(a, b, c)      mnc_join_ip_asm((a), (b), (c))
128#define mnc_join_ipv4_asm(a, b, c)      mnc_join_ip_asm((a), (b), (c))
129
130int mnc_join_ip_asm(int socket, struct addrinfo * group, char * iface)
131{
132	struct	group_req	multicast_request;
133	int			ip_proto;
134
135	if (group->ai_family == AF_INET6)
136	{
137		ip_proto = IPPROTO_IPV6;
138	}
139	else
140	{
141		ip_proto = IPPROTO_IP;
142	}
143
144	if (iface != NULL)
145	{
146		if ((multicast_request.gr_interface = if_nametoindex(iface))
147		    == 0)
148		{
149			mnc_warning("Ignoring unknown interface: %s\n", iface);
150		}
151	}
152	else
153	{
154		multicast_request.gr_interface = 0;
155	}
156
157	memcpy(&multicast_request.gr_group, group->ai_addr, group->ai_addrlen);
158
159	/* Set the socket option */
160	if (setsockopt(socket, ip_proto, MCAST_JOIN_GROUP, (char *)
161	               &multicast_request, sizeof(multicast_request)) != 0)
162	{
163		mnc_warning("Could not join the multicast group: %s\n",
164		            strerror(errno));
165
166		return -1;
167	}
168
169	return 0;
170}
171
172#endif /* MCAST_JOIN_GROUP */
173
174#ifndef MCAST_JOIN_SOURCE_GROUP
175int mnc_join_ipv4_asm(int socket, struct addrinfo * group, char * iface)
176{
177	struct	ip_mreq		multicast_request;
178
179	if (iface != NULL)
180	{
181		/* See if interface is a literal IPv4 address */
182		if ((multicast_request.imr_interface.s_addr =
183		     inet_addr(iface)) == INADDR_NONE)
184		{
185			mnc_warning("Invalid interface address\n");
186			return -1;
187		}
188	}
189	else
190	{
191		/* Set the interface to the default */
192		multicast_request.imr_interface.s_addr = htonl(INADDR_ANY);
193	}
194
195	multicast_request.imr_multiaddr.s_addr =
196	                ((struct sockaddr_in *)group->ai_addr)->sin_addr.s_addr;
197
198	/* Set the socket option */
199	if (setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
200	               (char *) &multicast_request,
201	               sizeof(multicast_request)) != 0)
202	{
203		mnc_warning("Could not join the multicast group: %s\n",
204		            strerror(errno));
205
206		return -1;
207	}
208
209	return 0;
210}
211
212int mnc_join_ipv6_asm(int socket, struct addrinfo * group, char * iface)
213{
214	mnc_warning("Sorry, No support for IPv6 any-source multicast in this build\n");
215
216	return -1;
217}
218#else /* if MCAST_JOIN_SOURCE_GROUP ... */
219
220#define mnc_join_ipv4_ssm(a, b, c, d)   mnc_join_ip_ssm((a), (b), (c), (d))
221#define mnc_join_ipv6_ssm(a, b, c, d)   mnc_join_ip_ssm((a), (b), (c), (d))
222
223int mnc_join_ip_ssm(int socket, struct addrinfo * group,
224                      struct addrinfo * source,
225                      char * iface)
226{
227	struct	group_source_req	multicast_request;
228	int				ip_proto;
229
230	if (group->ai_family == AF_INET6)
231	{
232		ip_proto = IPPROTO_IPV6;
233	}
234	else
235	{
236		ip_proto = IPPROTO_IP;
237	}
238
239	if (iface != NULL)
240	{
241		if ((multicast_request.gsr_interface = if_nametoindex(iface))
242		    == 0)
243		{
244			mnc_warning("Ignoring unknown interface: %s\n", iface);
245		}
246	}
247	else
248	{
249		multicast_request.gsr_interface = 0;
250	}
251
252	memcpy(&multicast_request.gsr_group, group->ai_addr, group->ai_addrlen);
253	memcpy(&multicast_request.gsr_source, source->ai_addr,
254	       source->ai_addrlen);
255
256	/* Set the socket option */
257	if (setsockopt(socket, ip_proto, MCAST_JOIN_SOURCE_GROUP,
258	               (char *) &multicast_request,
259	               sizeof(multicast_request)) != 0)
260	{
261		mnc_warning("Could not join the multicast group: %s\n",
262		            strerror(errno));
263
264		return -1;
265	}
266
267	return 0;
268}
269#endif /* MCAST_JOIN_SOURCE_GROUP */
270
271int multicast_setup_listen(int socket, struct addrinfo * group,
272                            struct addrinfo * source, char * iface)
273{
274        size_t rcvbuf;
275
276#ifndef WINDOWS
277	/* bind to the group address before anything */
278	if (bind(socket, group->ai_addr, group->ai_addrlen) != 0)
279	{
280		mnc_warning("Could not bind to group-id\n");
281		return -1;
282	}
283#else
284        if (group->ai_family == AF_INET)
285        {
286                struct sockaddr_in sin;
287
288                sin.sin_family = group->ai_family;
289                sin.sin_port = group->ai_port;
290                sin.sin_addr = INADDR_ANY;
291
292                if (bind(socket, (struct sockaddr *) sin,
293                         sizeof(sin)) != 0)
294                {
295                        mnc_warning("Could not bind to ::\n");
296                        return -1;
297                }
298        }
299        else if (group->ai_family == AF_INET6)
300        {
301                struct sockaddr_in6 sin6;
302
303                sin6.sin6_family = group->ai_family;
304                sin6.sin6_port = group->ai_port;
305                sin6.sin6_addr = in6addr_any;
306
307                if (bind(socket, (struct sockaddr *) sin6,
308                         sizeof(sin6)) != 0)
309                {
310                        mnc_warning("Could not bind to ::\n");
311                        return -1;
312                }
313        }
314#endif
315
316        /* Set a receive buffer size of 64k */
317        rcvbuf = 1 << 15;
318        if (setsockopt(socket, SOL_SOCKET, SO_RCVBUF, &rcvbuf,
319                       sizeof(rcvbuf)) < 0) {
320                mnc_warning("Could not set receive buffer to 64k\n");
321        }
322
323	if (source != NULL)
324	{
325		if (group->ai_family == AF_INET6)
326		{
327			/* Use whatever IPv6 API is appropriate */
328			return
329			    mnc_join_ipv6_ssm(socket, group, source, iface);
330		}
331		else if (group->ai_family == AF_INET)
332		{
333			/* Use the fully portable IPv4 API */
334			return
335			    mnc_join_ipv4_ssm(socket, group, source, iface);
336		}
337		else
338		{
339			mnc_warning("Only IPv4 and IPv6 are supported\n");
340			return -1;
341		}
342	}
343	else
344	{
345		if (group->ai_family == AF_INET6)
346		{
347			/* Use the fully portable IPv4 API */
348			return
349			    mnc_join_ipv6_asm(socket, group, iface);
350		}
351		else if (group->ai_family == AF_INET)
352		{
353			/* Use the fully portable IPv4 API */
354			return
355			    mnc_join_ipv4_asm(socket, group, iface);
356		}
357		else
358		{
359			mnc_warning("Only IPv4 and IPv6 are supported\n");
360			return -1;
361		}
362	}
363
364	/* We should never get here */
365	return -1;
366}
367
368
369int multicast_setup_send(int socket, struct addrinfo * group,
370                            struct addrinfo * source)
371{
372	int	ttl	= 255;
373
374	if (source != NULL)
375	{
376		/* bind to the address before anything */
377		if (bind(socket, source->ai_addr, source->ai_addrlen) != 0)
378		{
379			mnc_warning("Could not bind to source-address\n");
380			return -1;
381		}
382	}
383
384	if (group->ai_family == AF_INET)
385	{
386		if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, (char *)
387		               &ttl, sizeof(ttl)) != 0)
388		{
389			mnc_warning("Could not increase the TTL\n");
390			return -1;
391		}
392	}
393	else if (group->ai_family == AF_INET6)
394	{
395		if (setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
396		               (char *) &ttl, sizeof(ttl)) != 0)
397		{
398			mnc_warning("Could not increase the hop-count\n");
399			return -1;
400		}
401	}
402
403	return 0;
404}
405