build: include stdlib.h for atoi()
[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 <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, gboolean loop)
121 {
122   socklen_t socklen;
123   struct sockaddr_storage addr;
124   int ret = -1;
125   int l = (loop == FALSE) ? 0 : 1;
126
127   socklen = sizeof (addr);
128   if ((ret = getsockname (sockfd, (struct sockaddr *) &addr, &socklen)) < 0) {
129     return ret;
130   }
131
132   switch (addr.ss_family) {
133     case AF_INET:
134     {
135       ret = setsockopt (sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &l, sizeof (l));
136       if (ret < 0)
137         return ret;
138
139       break;
140     }
141     case AF_INET6:
142     {
143       ret =
144           setsockopt (sockfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &l,
145           sizeof (l));
146       if (ret < 0)
147         return ret;
148
149       break;
150     }
151     default:
152 #ifdef G_OS_WIN32
153       WSASetLastError (WSAEAFNOSUPPORT);
154 #else
155       errno = EAFNOSUPPORT;
156 #endif
157   }
158
159   return ret;
160 }
161
162 int
163 gst_udp_set_ttl (int sockfd, int ttl, gboolean is_multicast)
164 {
165   socklen_t socklen;
166   struct sockaddr_storage addr;
167   int optname = -1;
168   int ret = -1;
169
170   socklen = sizeof (addr);
171   if ((ret = getsockname (sockfd, (struct sockaddr *) &addr, &socklen)) < 0) {
172     return ret;
173   }
174
175   switch (addr.ss_family) {
176     case AF_INET:
177     {
178       optname = (is_multicast == TRUE) ? IP_MULTICAST_TTL : IP_TTL;
179       ret = setsockopt (sockfd, IPPROTO_IP, optname, &ttl, sizeof (ttl));
180       if (ret < 0)
181         return ret;
182       break;
183     }
184     case AF_INET6:
185     {
186       optname =
187           (is_multicast == TRUE) ? IPV6_MULTICAST_HOPS : IPV6_UNICAST_HOPS;
188       ret = setsockopt (sockfd, IPPROTO_IPV6, optname, &ttl, sizeof (ttl));
189       if (ret < 0)
190         return ret;
191
192       /* When using IPV4 address with IPV6 socket, both TTL values
193          must be set in order to actually use the given value.
194          Has no effect when IPV6 address is used. */
195       optname = (is_multicast == TRUE) ? IP_MULTICAST_TTL : IP_TTL;
196       ret = setsockopt (sockfd, IPPROTO_IP, optname, &ttl, sizeof (ttl));
197       if (ret < 0)
198         return ret;
199       break;
200     }
201     default:
202 #ifdef G_OS_WIN32
203       WSASetLastError (WSAEAFNOSUPPORT);
204 #else
205       errno = EAFNOSUPPORT;
206 #endif
207   }
208   return ret;
209 }
210
211 /* FIXME: Add interface selection for windows hosts.  */
212 int
213 gst_udp_join_group (int sockfd, struct sockaddr_storage *addr, gchar * iface)
214 {
215   int ret = -1;
216
217   switch (addr->ss_family) {
218     case AF_INET:
219     {
220 #ifdef HAVE_IP_MREQN
221       struct ip_mreqn mreq4;
222 #else
223       struct ip_mreq mreq4;
224 #endif
225
226       memset (&mreq4, 0, sizeof (mreq4));
227       mreq4.imr_multiaddr.s_addr =
228           ((struct sockaddr_in *) addr)->sin_addr.s_addr;
229 #ifdef HAVE_IP_MREQN
230       if (iface)
231         mreq4.imr_ifindex = if_nametoindex (iface);
232       else
233         mreq4.imr_ifindex = 0;  /* Pick any.  */
234 #else
235       mreq4.imr_interface.s_addr = INADDR_ANY;
236 #endif
237
238       if ((ret =
239               setsockopt (sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
240                   (const void *) &mreq4, sizeof (mreq4))) < 0)
241         return ret;
242
243       break;
244     }
245     case AF_INET6:
246     {
247       struct ipv6_mreq mreq6;
248
249       memset (&mreq6, 0, sizeof (mreq6));
250       memcpy (&mreq6.ipv6mr_multiaddr,
251           &(((struct sockaddr_in6 *) addr)->sin6_addr),
252           sizeof (struct in6_addr));
253       mreq6.ipv6mr_interface = 0;
254 #if !defined(G_OS_WIN32)
255       if (iface)
256         mreq6.ipv6mr_interface = if_nametoindex (iface);
257 #endif
258
259       if ((ret =
260               setsockopt (sockfd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
261                   (const void *) &mreq6, sizeof (mreq6))) < 0)
262         return ret;
263
264       break;
265     }
266     default:
267 #ifdef G_OS_WIN32
268       WSASetLastError (WSAEAFNOSUPPORT);
269 #else
270       errno = EAFNOSUPPORT;
271 #endif
272   }
273   return ret;
274 }
275
276 int
277 gst_udp_leave_group (int sockfd, struct sockaddr_storage *addr)
278 {
279   int ret = -1;
280
281   switch (addr->ss_family) {
282     case AF_INET:
283     {
284       struct ip_mreq mreq4;
285
286       memset (&mreq4, 0, sizeof (mreq4));
287       mreq4.imr_multiaddr.s_addr =
288           ((struct sockaddr_in *) addr)->sin_addr.s_addr;
289       mreq4.imr_interface.s_addr = INADDR_ANY;
290
291       if ((ret =
292               setsockopt (sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
293                   (const void *) &mreq4, sizeof (mreq4))) < 0)
294         return ret;
295     }
296       break;
297
298     case AF_INET6:
299     {
300       struct ipv6_mreq mreq6;
301
302       memset (&mreq6, 0, sizeof (mreq6));
303       memcpy (&mreq6.ipv6mr_multiaddr,
304           &(((struct sockaddr_in6 *) addr)->sin6_addr),
305           sizeof (struct in6_addr));
306       mreq6.ipv6mr_interface = 0;
307
308       if ((ret =
309               setsockopt (sockfd, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
310                   (const void *) &mreq6, sizeof (mreq6))) < 0)
311         return ret;
312     }
313       break;
314
315     default:
316 #ifdef G_OS_WIN32
317       WSASetLastError (WSAEAFNOSUPPORT);
318 #else
319       errno = EAFNOSUPPORT;
320 #endif
321   }
322
323   return ret;
324 }
325
326 int
327 gst_udp_is_multicast (struct sockaddr_storage *addr)
328 {
329   int ret = -1;
330
331   switch (addr->ss_family) {
332     case AF_INET:
333     {
334       struct sockaddr_in *addr4 = (struct sockaddr_in *) addr;
335
336       ret = IN_MULTICAST (g_ntohl (addr4->sin_addr.s_addr));
337     }
338       break;
339
340     case AF_INET6:
341     {
342       struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) addr;
343
344       ret = IN6_IS_ADDR_MULTICAST (&addr6->sin6_addr);
345     }
346       break;
347
348     default:
349 #ifdef G_OS_WIN32
350       WSASetLastError (WSAEAFNOSUPPORT);
351 #else
352       errno = EAFNOSUPPORT;
353 #endif
354   }
355
356   return ret;
357 }
358
359 void
360 gst_udp_uri_init (GstUDPUri * uri, const gchar * host, gint port)
361 {
362   uri->host = NULL;
363   uri->port = -1;
364   gst_udp_uri_update (uri, host, port);
365 }
366
367 int
368 gst_udp_uri_update (GstUDPUri * uri, const gchar * host, gint port)
369 {
370   if (host) {
371     g_free (uri->host);
372     uri->host = g_strdup (host);
373     if (strchr (host, ':'))
374       uri->is_ipv6 = TRUE;
375     else
376       uri->is_ipv6 = FALSE;
377   }
378   if (port != -1)
379     uri->port = port;
380
381   return 0;
382 }
383
384 int
385 gst_udp_parse_uri (const gchar * uristr, GstUDPUri * uri)
386 {
387   gchar *protocol;
388   gchar *location, *location_end;
389   gchar *colptr;
390
391   protocol = gst_uri_get_protocol (uristr);
392   if (strcmp (protocol, "udp") != 0)
393     goto wrong_protocol;
394   g_free (protocol);
395
396   location = gst_uri_get_location (uristr);
397   if (!location)
398     return FALSE;
399
400   GST_DEBUG ("got location '%s'", location);
401
402   if (location[0] == '[') {
403     GST_DEBUG ("parse IPV6 address '%s'", location);
404     location_end = strchr (location, ']');
405     if (location_end == NULL)
406       goto wrong_address;
407
408     uri->is_ipv6 = TRUE;
409     g_free (uri->host);
410     uri->host = g_strndup (location + 1, location_end - location - 1);
411     colptr = strrchr (location_end, ':');
412   } else {
413     GST_DEBUG ("parse IPV4 address '%s'", location);
414     uri->is_ipv6 = FALSE;
415     colptr = strrchr (location, ':');
416
417     g_free (uri->host);
418     if (colptr != NULL) {
419       uri->host = g_strndup (location, colptr - location);
420     } else {
421       uri->host = g_strdup (location);
422     }
423   }
424   GST_DEBUG ("host set to '%s'", uri->host);
425
426   if (colptr != NULL) {
427     uri->port = atoi (colptr + 1);
428   }
429   g_free (location);
430
431   return 0;
432
433   /* ERRORS */
434 wrong_protocol:
435   {
436     GST_ERROR ("error parsing uri %s: wrong protocol (%s != udp)", uristr,
437         protocol);
438     g_free (protocol);
439     return -1;
440   }
441 wrong_address:
442   {
443     GST_ERROR ("error parsing uri %s", uristr);
444     g_free (location);
445     return -1;
446   }
447 }
448
449 gchar *
450 gst_udp_uri_string (GstUDPUri * uri)
451 {
452   gchar *result;
453
454   if (uri->is_ipv6) {
455     result = g_strdup_printf ("udp://[%s]:%d", uri->host, uri->port);
456   } else {
457     result = g_strdup_printf ("udp://%s:%d", uri->host, uri->port);
458   }
459   return result;
460 }
461
462 void
463 gst_udp_uri_free (GstUDPUri * uri)
464 {
465   g_free (uri->host);
466   uri->host = NULL;
467   uri->port = -1;
468 }