udpsink: Add ttl multicast property
[platform/upstream/gst-plugins-good.git] / gst / udp / gstudpnetutils.c
1 /* GStreamer UDP network utility functions
2  * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
3  * Copyright (C) 2006 Joni Valtanen <joni.valtanen@movial.fi>
4  * Copyright (C) 2009 Jarkko Palviainen <jarkko.palviainen@sesca.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <errno.h>
27 #include <stdio.h>
28 #include <memory.h>
29
30 #include "gstudpnetutils.h"
31
32 /* EAI_ADDRFAMILY was obsoleted in BSD at some point */
33 #ifndef EAI_ADDRFAMILY
34 #define EAI_ADDRFAMILY 1
35 #endif
36
37 #ifdef G_OS_WIN32
38
39 gboolean
40 gst_udp_net_utils_win32_wsa_startup (GstObject * obj)
41 {
42   WSADATA w;
43   int error;
44
45   error = WSAStartup (0x0202, &w);
46
47   if (error) {
48     GST_WARNING_OBJECT (obj, "WSAStartup error: %d", error);
49     return FALSE;
50   }
51
52   if (w.wVersion != 0x0202) {
53     WSACleanup ();
54     GST_WARNING_OBJECT (obj, "Winsock version wrong : 0x%x", w.wVersion);
55     return FALSE;
56   }
57
58   return TRUE;
59 }
60
61 #endif
62
63 int
64 gst_udp_get_sockaddr_length (struct sockaddr_storage *addr)
65 {
66   /* MacOS is picky about passing precisely the correct length,
67    * so we calculate it here for the given socket type.
68    */
69   switch (addr->ss_family) {
70     case AF_INET:
71       return sizeof (struct sockaddr_in);
72     case AF_INET6:
73       return sizeof (struct sockaddr_in6);
74     default:
75       /* don't know, Screw MacOS and use the full length */
76       return sizeof (*addr);
77   }
78 }
79
80 int
81 gst_udp_get_addr (const char *hostname, int port, struct sockaddr_storage *addr)
82 {
83   struct addrinfo hints, *res = NULL, *nres;
84   char service[NI_MAXSERV];
85   int ret;
86
87   memset (&hints, 0, sizeof (hints));
88   hints.ai_family = AF_UNSPEC;
89   hints.ai_socktype = SOCK_DGRAM;
90   g_snprintf (service, sizeof (service) - 1, "%d", port);
91   service[sizeof (service) - 1] = '\0';
92
93   if ((ret = getaddrinfo (hostname, (port == -1) ? NULL : service, &hints,
94               &res)) < 0) {
95     goto beach;
96   }
97
98   nres = res;
99   while (nres) {
100     if (nres->ai_family == AF_INET || nres->ai_family == AF_INET6)
101       break;
102     nres = nres->ai_next;
103   }
104
105   if (nres) {
106     memcpy (addr, nres->ai_addr, nres->ai_addrlen);
107   } else {
108     ret = EAI_ADDRFAMILY;
109   }
110
111   freeaddrinfo (res);
112 beach:
113   return ret;
114 }
115
116 int
117 gst_udp_set_loop (int sockfd, gboolean loop)
118 {
119   socklen_t socklen;
120   struct sockaddr_storage addr;
121   int ret = -1;
122   int l = (loop == FALSE) ? 0 : 1;
123
124   socklen = sizeof (addr);
125   if ((ret = getsockname (sockfd, (struct sockaddr *) &addr, &socklen)) < 0) {
126     return ret;
127   }
128
129   switch (addr.ss_family) {
130     case AF_INET:
131     {
132       ret = setsockopt (sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &l, sizeof (l));
133       if (ret < 0)
134         return ret;
135
136       break;
137     }
138     case AF_INET6:
139     {
140       ret =
141           setsockopt (sockfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &l,
142           sizeof (l));
143       if (ret < 0)
144         return ret;
145
146       break;
147     }
148     default:
149 #ifdef G_OS_WIN32
150       WSASetLastError (WSAEAFNOSUPPORT);
151 #else
152       errno = EAFNOSUPPORT;
153 #endif
154   }
155
156   return ret;
157 }
158
159 int
160 gst_udp_set_ttl (int sockfd, int ttl, gboolean is_multicast)
161 {
162   socklen_t socklen;
163   struct sockaddr_storage addr;
164   int optname = -1;
165   int ret = -1;
166
167   socklen = sizeof (addr);
168   if ((ret = getsockname (sockfd, (struct sockaddr *) &addr, &socklen)) < 0) {
169     return ret;
170   }
171
172   switch (addr.ss_family) {
173     case AF_INET:
174     {
175       optname = (is_multicast == TRUE) ? IP_MULTICAST_TTL : IP_TTL;
176       ret = setsockopt (sockfd, IPPROTO_IP, optname, &ttl, sizeof (ttl));
177       if (ret < 0)
178         return ret;
179       break;
180     }
181     case AF_INET6:
182     {
183       optname =
184           (is_multicast == TRUE) ? IPV6_MULTICAST_HOPS : IPV6_UNICAST_HOPS;
185       ret = setsockopt (sockfd, IPPROTO_IPV6, optname, &ttl, sizeof (ttl));
186       if (ret < 0)
187         return ret;
188
189       /* When using IPV4 address with IPV6 socket, both TTL values
190          must be set in order to actually use the given value.
191          Has no effect when IPV6 address is used. */
192       optname = (is_multicast == TRUE) ? IP_MULTICAST_TTL : IP_TTL;
193       ret = setsockopt (sockfd, IPPROTO_IP, optname, &ttl, sizeof (ttl));
194       if (ret < 0)
195         return ret;
196       break;
197     }
198     default:
199 #ifdef G_OS_WIN32
200       WSASetLastError (WSAEAFNOSUPPORT);
201 #else
202       errno = EAFNOSUPPORT;
203 #endif
204   }
205   return ret;
206 }
207
208 /* FIXME: Add interface selection for windows hosts.  */
209 int
210 gst_udp_join_group (int sockfd, struct sockaddr_storage *addr, gchar * iface)
211 {
212   int ret = -1;
213
214   switch (addr->ss_family) {
215     case AF_INET:
216     {
217 #ifdef HAVE_IP_MREQN
218       struct ip_mreqn mreq4;
219 #else
220       struct ip_mreq mreq4;
221 #endif
222
223       memset (&mreq4, 0, sizeof (mreq4));
224       mreq4.imr_multiaddr.s_addr =
225           ((struct sockaddr_in *) addr)->sin_addr.s_addr;
226 #ifdef HAVE_IP_MREQN
227       if (iface)
228         mreq4.imr_ifindex = if_nametoindex (iface);
229       else
230         mreq4.imr_ifindex = 0;  /* Pick any.  */
231 #else
232       mreq4.imr_interface.s_addr = INADDR_ANY;
233 #endif
234
235       if ((ret =
236               setsockopt (sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
237                   (const void *) &mreq4, sizeof (mreq4))) < 0)
238         return ret;
239
240       break;
241     }
242     case AF_INET6:
243     {
244       struct ipv6_mreq mreq6;
245
246       memset (&mreq6, 0, sizeof (mreq6));
247       memcpy (&mreq6.ipv6mr_multiaddr,
248           &(((struct sockaddr_in6 *) addr)->sin6_addr),
249           sizeof (struct in6_addr));
250       mreq6.ipv6mr_interface = 0;
251 #if !defined(G_OS_WIN32)
252       if (iface)
253         mreq6.ipv6mr_interface = if_nametoindex (iface);
254 #endif
255
256       if ((ret =
257               setsockopt (sockfd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
258                   (const void *) &mreq6, sizeof (mreq6))) < 0)
259         return ret;
260
261       break;
262     }
263     default:
264 #ifdef G_OS_WIN32
265       WSASetLastError (WSAEAFNOSUPPORT);
266 #else
267       errno = EAFNOSUPPORT;
268 #endif
269   }
270   return ret;
271 }
272
273 int
274 gst_udp_leave_group (int sockfd, struct sockaddr_storage *addr)
275 {
276   int ret = -1;
277
278   switch (addr->ss_family) {
279     case AF_INET:
280     {
281       struct ip_mreq mreq4;
282
283       memset (&mreq4, 0, sizeof (mreq4));
284       mreq4.imr_multiaddr.s_addr =
285           ((struct sockaddr_in *) addr)->sin_addr.s_addr;
286       mreq4.imr_interface.s_addr = INADDR_ANY;
287
288       if ((ret =
289               setsockopt (sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
290                   (const void *) &mreq4, sizeof (mreq4))) < 0)
291         return ret;
292     }
293       break;
294
295     case AF_INET6:
296     {
297       struct ipv6_mreq mreq6;
298
299       memset (&mreq6, 0, sizeof (mreq6));
300       memcpy (&mreq6.ipv6mr_multiaddr,
301           &(((struct sockaddr_in6 *) addr)->sin6_addr),
302           sizeof (struct in6_addr));
303       mreq6.ipv6mr_interface = 0;
304
305       if ((ret =
306               setsockopt (sockfd, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
307                   (const void *) &mreq6, sizeof (mreq6))) < 0)
308         return ret;
309     }
310       break;
311
312     default:
313 #ifdef G_OS_WIN32
314       WSASetLastError (WSAEAFNOSUPPORT);
315 #else
316       errno = EAFNOSUPPORT;
317 #endif
318   }
319
320   return ret;
321 }
322
323 int
324 gst_udp_is_multicast (struct sockaddr_storage *addr)
325 {
326   int ret = -1;
327
328   switch (addr->ss_family) {
329     case AF_INET:
330     {
331       struct sockaddr_in *addr4 = (struct sockaddr_in *) addr;
332
333       ret = IN_MULTICAST (g_ntohl (addr4->sin_addr.s_addr));
334     }
335       break;
336
337     case AF_INET6:
338     {
339       struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) addr;
340
341       ret = IN6_IS_ADDR_MULTICAST (&addr6->sin6_addr);
342     }
343       break;
344
345     default:
346 #ifdef G_OS_WIN32
347       WSASetLastError (WSAEAFNOSUPPORT);
348 #else
349       errno = EAFNOSUPPORT;
350 #endif
351   }
352
353   return ret;
354 }