Tizen 2.0 Release
[framework/multimedia/gst-plugins-good0.10.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 <stdlib.h>
29 #include <memory.h>
30
31 #include <gst/gst.h>
32
33 #include "gstudpnetutils.h"
34
35 /* EAI_ADDRFAMILY was obsoleted in BSD at some point */
36 #ifndef EAI_ADDRFAMILY
37 #define EAI_ADDRFAMILY 1
38 #endif
39
40 #ifdef G_OS_WIN32
41
42 gboolean
43 gst_udp_net_utils_win32_wsa_startup (GstObject * obj)
44 {
45   WSADATA w;
46   int error;
47
48   error = WSAStartup (0x0202, &w);
49
50   if (error) {
51     GST_WARNING_OBJECT (obj, "WSAStartup error: %d", error);
52     return FALSE;
53   }
54
55   if (w.wVersion != 0x0202) {
56     WSACleanup ();
57     GST_WARNING_OBJECT (obj, "Winsock version wrong : 0x%x", w.wVersion);
58     return FALSE;
59   }
60
61   return TRUE;
62 }
63
64 #endif
65
66 int
67 gst_udp_get_sockaddr_length (struct sockaddr_storage *addr)
68 {
69   /* MacOS is picky about passing precisely the correct length,
70    * so we calculate it here for the given socket type.
71    */
72   switch (addr->ss_family) {
73     case AF_INET:
74       return sizeof (struct sockaddr_in);
75     case AF_INET6:
76       return sizeof (struct sockaddr_in6);
77     default:
78       /* don't know, Screw MacOS and use the full length */
79       return sizeof (*addr);
80   }
81 }
82
83 int
84 gst_udp_get_addr (const char *hostname, int port, struct sockaddr_storage *addr)
85 {
86   struct addrinfo hints, *res = NULL, *nres;
87   char service[NI_MAXSERV];
88   int ret;
89
90   memset (&hints, 0, sizeof (hints));
91   hints.ai_family = AF_UNSPEC;
92   hints.ai_socktype = SOCK_DGRAM;
93   g_snprintf (service, sizeof (service) - 1, "%d", port);
94   service[sizeof (service) - 1] = '\0';
95
96   if ((ret = getaddrinfo (hostname, (port == -1) ? NULL : service, &hints,
97               &res)) < 0) {
98     goto beach;
99   }
100
101   nres = res;
102   while (nres) {
103     if (nres->ai_family == AF_INET || nres->ai_family == AF_INET6)
104       break;
105     nres = nres->ai_next;
106   }
107
108   if (nres) {
109     memcpy (addr, nres->ai_addr, nres->ai_addrlen);
110   } else {
111     ret = EAI_ADDRFAMILY;
112   }
113
114   freeaddrinfo (res);
115 beach:
116   return ret;
117 }
118
119 int
120 gst_udp_set_loop (int sockfd, guint16 ss_family, gboolean loop)
121 {
122   int ret = -1;
123   int l = (loop == FALSE) ? 0 : 1;
124
125   switch (ss_family) {
126     case AF_INET:
127     {
128       ret = setsockopt (sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &l, sizeof (l));
129       if (ret < 0)
130         return ret;
131
132       break;
133     }
134     case AF_INET6:
135     {
136       ret =
137           setsockopt (sockfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &l,
138           sizeof (l));
139       if (ret < 0)
140         return ret;
141
142       break;
143     }
144     default:
145 #ifdef G_OS_WIN32
146       WSASetLastError (WSAEAFNOSUPPORT);
147 #else
148       errno = EAFNOSUPPORT;
149 #endif
150   }
151
152   return ret;
153 }
154
155 int
156 gst_udp_set_ttl (int sockfd, guint16 ss_family, int ttl, gboolean is_multicast)
157 {
158   int optname = -1;
159   int ret = -1;
160
161   switch (ss_family) {
162     case AF_INET:
163     {
164       optname = (is_multicast == TRUE) ? IP_MULTICAST_TTL : IP_TTL;
165       ret = setsockopt (sockfd, IPPROTO_IP, optname, &ttl, sizeof (ttl));
166       if (ret < 0)
167         return ret;
168       break;
169     }
170     case AF_INET6:
171     {
172       optname =
173           (is_multicast == TRUE) ? IPV6_MULTICAST_HOPS : IPV6_UNICAST_HOPS;
174       ret = setsockopt (sockfd, IPPROTO_IPV6, optname, &ttl, sizeof (ttl));
175       if (ret < 0)
176         return ret;
177
178       /* When using IPV4 address with IPV6 socket, both TTL values
179          must be set in order to actually use the given value.
180          Has no effect when IPV6 address is used. */
181       optname = (is_multicast == TRUE) ? IP_MULTICAST_TTL : IP_TTL;
182       ret = setsockopt (sockfd, IPPROTO_IP, optname, &ttl, sizeof (ttl));
183       if (ret < 0)
184         return ret;
185       break;
186     }
187     default:
188 #ifdef G_OS_WIN32
189       WSASetLastError (WSAEAFNOSUPPORT);
190 #else
191       errno = EAFNOSUPPORT;
192 #endif
193   }
194   return ret;
195 }
196
197 /* FIXME: Add interface selection for windows hosts.  */
198 int
199 gst_udp_join_group (int sockfd, struct sockaddr_storage *addr, gchar * iface)
200 {
201   int ret = -1;
202
203   switch (addr->ss_family) {
204     case AF_INET:
205     {
206 #ifdef HAVE_IP_MREQN
207       struct ip_mreqn mreq4;
208 #else
209       struct ip_mreq mreq4;
210 #endif
211
212       memset (&mreq4, 0, sizeof (mreq4));
213       mreq4.imr_multiaddr.s_addr =
214           ((struct sockaddr_in *) addr)->sin_addr.s_addr;
215 #ifdef HAVE_IP_MREQN
216       if (iface)
217         mreq4.imr_ifindex = if_nametoindex (iface);
218       else
219         mreq4.imr_ifindex = 0;  /* Pick any.  */
220 #else
221       mreq4.imr_interface.s_addr = INADDR_ANY;
222 #endif
223
224       if ((ret =
225               setsockopt (sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
226                   (const void *) &mreq4, sizeof (mreq4))) < 0)
227         return ret;
228
229       break;
230     }
231     case AF_INET6:
232     {
233       struct ipv6_mreq mreq6;
234
235       memset (&mreq6, 0, sizeof (mreq6));
236       memcpy (&mreq6.ipv6mr_multiaddr,
237           &(((struct sockaddr_in6 *) addr)->sin6_addr),
238           sizeof (struct in6_addr));
239       mreq6.ipv6mr_interface = 0;
240 #if !defined(G_OS_WIN32)
241       if (iface)
242         mreq6.ipv6mr_interface = if_nametoindex (iface);
243 #endif
244
245       if ((ret =
246               setsockopt (sockfd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
247                   (const void *) &mreq6, sizeof (mreq6))) < 0)
248         return ret;
249
250       break;
251     }
252     default:
253 #ifdef G_OS_WIN32
254       WSASetLastError (WSAEAFNOSUPPORT);
255 #else
256       errno = EAFNOSUPPORT;
257 #endif
258   }
259   return ret;
260 }
261
262 int
263 gst_udp_leave_group (int sockfd, struct sockaddr_storage *addr)
264 {
265   int ret = -1;
266
267   switch (addr->ss_family) {
268     case AF_INET:
269     {
270       struct ip_mreq mreq4;
271
272       memset (&mreq4, 0, sizeof (mreq4));
273       mreq4.imr_multiaddr.s_addr =
274           ((struct sockaddr_in *) addr)->sin_addr.s_addr;
275       mreq4.imr_interface.s_addr = INADDR_ANY;
276
277       if ((ret =
278               setsockopt (sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
279                   (const void *) &mreq4, sizeof (mreq4))) < 0)
280         return ret;
281     }
282       break;
283
284     case AF_INET6:
285     {
286       struct ipv6_mreq mreq6;
287
288       memset (&mreq6, 0, sizeof (mreq6));
289       memcpy (&mreq6.ipv6mr_multiaddr,
290           &(((struct sockaddr_in6 *) addr)->sin6_addr),
291           sizeof (struct in6_addr));
292       mreq6.ipv6mr_interface = 0;
293
294       if ((ret =
295               setsockopt (sockfd, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
296                   (const void *) &mreq6, sizeof (mreq6))) < 0)
297         return ret;
298     }
299       break;
300
301     default:
302 #ifdef G_OS_WIN32
303       WSASetLastError (WSAEAFNOSUPPORT);
304 #else
305       errno = EAFNOSUPPORT;
306 #endif
307   }
308
309   return ret;
310 }
311
312 int
313 gst_udp_is_multicast (struct sockaddr_storage *addr)
314 {
315   int ret = -1;
316
317   switch (addr->ss_family) {
318     case AF_INET:
319     {
320       struct sockaddr_in *addr4 = (struct sockaddr_in *) addr;
321
322       ret = IN_MULTICAST (g_ntohl (addr4->sin_addr.s_addr));
323     }
324       break;
325
326     case AF_INET6:
327     {
328       struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) addr;
329
330       ret = IN6_IS_ADDR_MULTICAST (&addr6->sin6_addr);
331     }
332       break;
333
334     default:
335 #ifdef G_OS_WIN32
336       WSASetLastError (WSAEAFNOSUPPORT);
337 #else
338       errno = EAFNOSUPPORT;
339 #endif
340   }
341
342   return ret;
343 }
344
345 void
346 gst_udp_uri_init (GstUDPUri * uri, const gchar * host, gint port)
347 {
348   uri->host = NULL;
349   uri->port = -1;
350   gst_udp_uri_update (uri, host, port);
351 }
352
353 int
354 gst_udp_uri_update (GstUDPUri * uri, const gchar * host, gint port)
355 {
356   if (host) {
357     g_free (uri->host);
358     uri->host = g_strdup (host);
359     if (strchr (host, ':'))
360       uri->is_ipv6 = TRUE;
361     else
362       uri->is_ipv6 = FALSE;
363   }
364   if (port != -1)
365     uri->port = port;
366
367   return 0;
368 }
369
370 int
371 gst_udp_parse_uri (const gchar * uristr, GstUDPUri * uri)
372 {
373   gchar *protocol, *location_start;
374   gchar *location, *location_end;
375   gchar *colptr;
376
377   /* consider no protocol to be udp:// */
378   protocol = gst_uri_get_protocol (uristr);
379   if (!protocol)
380     goto no_protocol;
381   if (strcmp (protocol, "udp") != 0)
382     goto wrong_protocol;
383   g_free (protocol);
384
385   location_start = gst_uri_get_location (uristr);
386   if (!location_start)
387     return FALSE;
388
389   GST_DEBUG ("got location '%s'", location_start);
390
391   /* VLC compatibility, strip everything before the @ sign. VLC uses that as the
392    * remote address. */
393   location = g_strstr_len (location_start, -1, "@");
394   if (location == NULL)
395     location = location_start;
396   else
397     location += 1;
398
399   if (location[0] == '[') {
400     GST_DEBUG ("parse IPV6 address '%s'", location);
401     location_end = strchr (location, ']');
402     if (location_end == NULL)
403       goto wrong_address;
404
405     uri->is_ipv6 = TRUE;
406     g_free (uri->host);
407     uri->host = g_strndup (location + 1, location_end - location - 1);
408     colptr = strrchr (location_end, ':');
409   } else {
410     GST_DEBUG ("parse IPV4 address '%s'", location);
411     uri->is_ipv6 = FALSE;
412     colptr = strrchr (location, ':');
413
414     g_free (uri->host);
415     if (colptr != NULL) {
416       uri->host = g_strndup (location, colptr - location);
417     } else {
418       uri->host = g_strdup (location);
419     }
420   }
421   GST_DEBUG ("host set to '%s'", uri->host);
422
423   if (colptr != NULL) {
424     uri->port = atoi (colptr + 1);
425   }
426   g_free (location_start);
427
428   return 0;
429
430   /* ERRORS */
431 no_protocol:
432   {
433     GST_ERROR ("error parsing uri %s: no protocol", uristr);
434     return -1;
435   }
436 wrong_protocol:
437   {
438     GST_ERROR ("error parsing uri %s: wrong protocol (%s != udp)", uristr,
439         protocol);
440     g_free (protocol);
441     return -1;
442   }
443 wrong_address:
444   {
445     GST_ERROR ("error parsing uri %s", uristr);
446     g_free (location);
447     return -1;
448   }
449 }
450
451 gchar *
452 gst_udp_uri_string (GstUDPUri * uri)
453 {
454   gchar *result;
455
456   if (uri->is_ipv6) {
457     result = g_strdup_printf ("udp://[%s]:%d", uri->host, uri->port);
458   } else {
459     result = g_strdup_printf ("udp://%s:%d", uri->host, uri->port);
460   }
461   return result;
462 }
463
464 void
465 gst_udp_uri_free (GstUDPUri * uri)
466 {
467   g_free (uri->host);
468   uri->host = NULL;
469   uri->port = -1;
470 }