]> git.sesse.net Git - vlc/blob - src/stream_output/sap.c
mux: avi: handle failed reallocs
[vlc] / src / stream_output / sap.c
1 /*****************************************************************************
2  * sap.c : SAP announce handler
3  *****************************************************************************
4  * Copyright (C) 2002-2008 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Clément Stenac <zorglub@videolan.org>
8  *          Rémi Denis-Courmont <rem # videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <vlc_common.h>
30
31 #include <stdlib.h>                                                /* free() */
32 #include <stdio.h>                                              /* sprintf() */
33 #include <string.h>
34 #include <assert.h>
35
36 #include <vlc_sout.h>
37 #include <vlc_network.h>
38
39 #include "stream_output.h"
40 #include "libvlc.h"
41
42 /* SAP is always on that port */
43 #define IPPORT_SAP 9875
44
45 /* A SAP session descriptor, enqueued in the SAP handler queue */
46 struct session_descriptor_t
47 {
48     struct session_descriptor_t *next;
49     size_t  length;
50     uint8_t data[];
51 };
52
53 /* A SAP announce address. For each of these, we run the
54  * control flow algorithm */
55 typedef struct sap_address_t
56 {
57     struct sap_address_t   *next;
58
59     vlc_thread_t            thread;
60     vlc_mutex_t             lock;
61     vlc_cond_t              wait;
62
63     char                    group[NI_MAXNUMERICHOST];
64     struct sockaddr_storage orig;
65     socklen_t               origlen;
66     int                     fd;
67     unsigned                interval;
68
69     unsigned                session_count;
70     session_descriptor_t   *first;
71 } sap_address_t;
72
73 static sap_address_t *sap_addrs = NULL;
74 static vlc_mutex_t sap_mutex = VLC_STATIC_MUTEX;
75
76 #define SAP_MAX_BUFFER 65534
77 #define MIN_INTERVAL 2
78 #define MAX_INTERVAL 300
79
80 static void *RunThread (void *);
81
82 static sap_address_t *AddressCreate (vlc_object_t *obj, const char *group)
83 {
84     int fd = net_ConnectUDP (obj, group, IPPORT_SAP, 255);
85     if (fd == -1)
86         return NULL;
87
88     sap_address_t *addr = malloc (sizeof (*addr));
89     if (addr == NULL)
90     {
91         net_Close (fd);
92         return NULL;
93     }
94
95     strlcpy (addr->group, group, sizeof (addr->group));
96     addr->fd = fd;
97     addr->origlen = sizeof (addr->orig);
98     getsockname (fd, (struct sockaddr *)&addr->orig, &addr->origlen);
99
100     addr->interval = var_CreateGetInteger (obj, "sap-interval");
101     vlc_mutex_init (&addr->lock);
102     vlc_cond_init (&addr->wait);
103     addr->session_count = 0;
104     addr->first = NULL;
105
106     if (vlc_clone (&addr->thread, RunThread, addr, VLC_THREAD_PRIORITY_LOW))
107     {
108         msg_Err (obj, "unable to spawn SAP announce thread");
109         net_Close (fd);
110         free (addr);
111         return NULL;
112     }
113     return addr;
114 }
115
116 static void AddressDestroy (sap_address_t *addr)
117 {
118     assert (addr->first == NULL);
119
120     vlc_cancel (addr->thread);
121     vlc_join (addr->thread, NULL);
122     vlc_cond_destroy (&addr->wait);
123     vlc_mutex_destroy (&addr->lock);
124     net_Close (addr->fd);
125     free (addr);
126 }
127
128 /**
129  * main SAP handler thread
130  * \param p_this the SAP Handler object
131  * \return nothing
132  */
133 VLC_NORETURN
134 static void *RunThread (void *self)
135 {
136     sap_address_t *addr = self;
137
138     vlc_mutex_lock (&addr->lock);
139     mutex_cleanup_push (&addr->lock);
140
141     for (;;)
142     {
143         session_descriptor_t *p_session;
144         mtime_t deadline;
145
146         while (addr->first == NULL)
147             vlc_cond_wait (&addr->wait, &addr->lock);
148
149         assert (addr->session_count > 0);
150
151         deadline = mdate ();
152         for (p_session = addr->first; p_session; p_session = p_session->next)
153         {
154             send (addr->fd, p_session->data, p_session->length, 0);
155             deadline += addr->interval * CLOCK_FREQ / addr->session_count;
156
157             if (vlc_cond_timedwait (&addr->wait, &addr->lock, deadline) == 0)
158                 break; /* list may have changed! */
159         }
160     }
161
162     vlc_cleanup_pop ();
163     vlc_assert_unreachable ();
164 }
165
166 #undef sout_AnnounceRegisterSDP
167 /**
168  *  Registers a new session with the announce handler, using a pregenerated SDP
169  *
170  * \param obj a VLC object
171  * \param sdp the SDP to register
172  * \param dst session address (needed for SAP address auto detection)
173  * \return the new session descriptor structure
174  */
175 session_descriptor_t *
176 sout_AnnounceRegisterSDP (vlc_object_t *obj, const char *sdp,
177                           const char *dst)
178 {
179     int i;
180     char psz_addr[NI_MAXNUMERICHOST];
181     union
182     {
183         struct sockaddr     a;
184         struct sockaddr_in  in;
185         struct sockaddr_in6 in6;
186     } addr;
187     socklen_t addrlen = 0;
188     struct addrinfo *res;
189
190     msg_Dbg (obj, "adding SAP session");
191
192     if (vlc_getaddrinfo (dst, 0, NULL, &res) == 0)
193     {
194         if (res->ai_addrlen <= sizeof (addr))
195             memcpy (&addr, res->ai_addr, res->ai_addrlen);
196         addrlen = res->ai_addrlen;
197         freeaddrinfo (res);
198     }
199
200     if (addrlen == 0 || addrlen > sizeof (addr))
201     {
202         msg_Err (obj, "No/invalid address specified for SAP announce" );
203         return NULL;
204     }
205
206     /* Determine SAP multicast address automatically */
207     switch (addr.a.sa_family)
208     {
209 #if defined (HAVE_INET_PTON) || defined (_WIN32)
210         case AF_INET6:
211         {
212             /* See RFC3513 for list of valid IPv6 scopes */
213             struct in6_addr *a6 = &addr.in6.sin6_addr;
214
215             memcpy( a6->s6_addr + 2, "\x00\x00\x00\x00\x00\x00"
216                    "\x00\x00\x00\x00\x00\x02\x7f\xfe", 14 );
217             if( IN6_IS_ADDR_MULTICAST( a6 ) )
218                 /* force flags to zero, preserve scope */
219                 a6->s6_addr[1] &= 0xf;
220             else
221                 /* Unicast IPv6 - assume global scope */
222                 memcpy( a6->s6_addr, "\xff\x0e", 2 );
223             break;
224         }
225 #endif
226
227         case AF_INET:
228         {
229             /* See RFC2365 for IPv4 scopes */
230             uint32_t ipv4 = addr.in.sin_addr.s_addr;
231
232             /* 224.0.0.0/24 => 224.0.0.255 */
233             if ((ipv4 & htonl (0xffffff00)) == htonl (0xe0000000))
234                 ipv4 =  htonl (0xe00000ff);
235             else
236             /* 239.255.0.0/16 => 239.255.255.255 */
237             if ((ipv4 & htonl (0xffff0000)) == htonl (0xefff0000))
238                 ipv4 =  htonl (0xefffffff);
239             else
240             /* 239.192.0.0/14 => 239.195.255.255 */
241             if ((ipv4 & htonl (0xfffc0000)) == htonl (0xefc00000))
242                 ipv4 =  htonl (0xefc3ffff);
243             else
244             if ((ipv4 & htonl (0xff000000)) == htonl (0xef000000))
245                 ipv4 = 0;
246             else
247             /* other addresses => 224.2.127.254 */
248                 ipv4 = htonl (0xe0027ffe);
249
250             if( ipv4 == 0 )
251             {
252                 msg_Err (obj, "Out-of-scope multicast address "
253                          "not supported by SAP");
254                 return NULL;
255             }
256
257             addr.in.sin_addr.s_addr = ipv4;
258             break;
259         }
260
261         default:
262             msg_Err (obj, "Address family %d not supported by SAP",
263                      addr.a.sa_family);
264             return NULL;
265     }
266
267     i = vlc_getnameinfo( &addr.a, addrlen,
268                          psz_addr, sizeof( psz_addr ), NULL, NI_NUMERICHOST );
269
270     if( i )
271     {
272         msg_Err (obj, "%s", gai_strerror (i));
273         return NULL;
274     }
275
276     /* Find/create SAP address thread */
277     sap_address_t *sap_addr;
278
279     msg_Dbg (obj, "using SAP address: %s", psz_addr);
280     vlc_mutex_lock (&sap_mutex);
281     for (sap_addr = sap_addrs; sap_addr; sap_addr = sap_addr->next)
282         if (!strcmp (psz_addr, sap_addr->group))
283             break;
284
285     if (sap_addr == NULL)
286     {
287         sap_addr = AddressCreate (obj, psz_addr);
288         if (sap_addr == NULL)
289         {
290             vlc_mutex_unlock (&sap_mutex);
291             return NULL;
292         }
293         sap_addr->next = sap_addrs;
294         sap_addrs = sap_addr;
295     }
296     /* Switch locks.
297      * NEVER take the global SAP lock when holding a SAP thread lock! */
298     vlc_mutex_lock (&sap_addr->lock);
299     vlc_mutex_unlock (&sap_mutex);
300
301     size_t length = 20;
302     switch (sap_addr->orig.ss_family)
303     {
304 #ifdef AF_INET6
305         case AF_INET6:
306             length += 16;
307             break;
308 #endif
309         case AF_INET:
310             length += 4;
311             break;
312         default:
313             vlc_assert_unreachable ();
314     }
315
316     /* XXX: Check for dupes */
317     length += strlen (sdp);
318
319     session_descriptor_t *session = malloc (sizeof (*session) + length);
320     if (unlikely(session == NULL))
321     {
322         vlc_mutex_unlock (&sap_addr->lock);
323         return NULL; /* NOTE: we should destroy the thread if left unused */
324     }
325     session->next = sap_addr->first;
326     sap_addr->first = session;
327     session->length = length;
328
329     /* Build the SAP Headers */
330     uint8_t *buf = session->data;
331
332     /* SAPv1, not encrypted, not compressed */
333     buf[0] = 0x20;
334     buf[1] = 0x00; /* No authentication length */
335     SetWBE(buf + 2, mdate()); /* ID hash */
336
337     size_t offset = 4;
338     switch (sap_addr->orig.ss_family)
339     {
340 #ifdef AF_INET6
341         case AF_INET6:
342         {
343             const struct in6_addr *a6 =
344                 &((const struct sockaddr_in6 *)&sap_addr->orig)->sin6_addr;
345             memcpy (buf + offset, a6, 16);
346             buf[0] |= 0x10; /* IPv6 flag */
347             offset += 16;
348             break;
349         }
350 #endif
351         case AF_INET:
352         {
353             const struct in_addr *a4 =
354                 &((const struct sockaddr_in *)&sap_addr->orig)->sin_addr;
355             memcpy (buf + offset, a4, 4);
356             offset += 4;
357             break;
358         }
359
360     }
361
362     memcpy (buf + offset, "application/sdp", 16);
363     offset += 16;
364
365     /* Build the final message */
366     strcpy((char *)buf + offset, sdp);
367
368     sap_addr->session_count++;
369     vlc_cond_signal (&sap_addr->wait);
370     vlc_mutex_unlock (&sap_addr->lock);
371     return session;
372 }
373
374 #undef sout_AnnounceUnRegister
375 /**
376  *  Unregisters an existing session
377  *
378  * \param obj a VLC object
379  * \param session the session descriptor
380  */
381 void sout_AnnounceUnRegister (vlc_object_t *obj, session_descriptor_t *session)
382 {
383     sap_address_t *addr, **paddr;
384     session_descriptor_t **psession;
385
386     msg_Dbg (obj, "removing SAP session");
387     vlc_mutex_lock (&sap_mutex);
388     paddr = &sap_addrs;
389     for (;;)
390     {
391         addr = *paddr;
392         assert (addr != NULL);
393
394         psession = &addr->first;
395         vlc_mutex_lock (&addr->lock);
396         while (*psession != NULL)
397         {
398             if (*psession == session)
399                 goto found;
400             psession = &(*psession)->next;
401         }
402         vlc_mutex_unlock (&addr->lock);
403         paddr = &addr->next;
404     }
405
406 found:
407     *psession = session->next;
408
409     if (addr->first == NULL)
410         /* Last session for this address -> unlink the address */
411         *paddr = addr->next;
412     vlc_mutex_unlock (&sap_mutex);
413
414     if (addr->first == NULL)
415     {
416         /* Last session for this address -> unlink the address */
417         vlc_mutex_unlock (&addr->lock);
418         AddressDestroy (addr);
419     }
420     else
421     {
422         addr->session_count--;
423         vlc_cond_signal (&addr->wait);
424         vlc_mutex_unlock (&addr->lock);
425     }
426
427     free (session);
428 }