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