1 /*****************************************************************************
2 * sap.c : SAP announce handler
3 *****************************************************************************
4 * Copyright (C) 2002-2008 VLC authors and VideoLAN
7 * Authors: Clément Stenac <zorglub@videolan.org>
8 * Rémi Denis-Courmont <rem # videolan.org>
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.
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.
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 *****************************************************************************/
29 #include <vlc_common.h>
31 #include <stdlib.h> /* free() */
32 #include <stdio.h> /* sprintf() */
37 #include <vlc_network.h>
39 #include "stream_output.h"
42 /* SAP is always on that port */
43 #define IPPORT_SAP 9875
45 /* A SAP session descriptor, enqueued in the SAP handler queue */
46 struct session_descriptor_t
48 struct session_descriptor_t *next;
53 /* A SAP announce address. For each of these, we run the
54 * control flow algorithm */
55 typedef struct sap_address_t
57 struct sap_address_t *next;
63 char group[NI_MAXNUMERICHOST];
64 struct sockaddr_storage orig;
69 unsigned session_count;
70 session_descriptor_t *first;
73 static sap_address_t *sap_addrs = NULL;
74 static vlc_mutex_t sap_mutex = VLC_STATIC_MUTEX;
76 #define SAP_MAX_BUFFER 65534
77 #define MIN_INTERVAL 2
78 #define MAX_INTERVAL 300
80 static void *RunThread (void *);
82 static sap_address_t *AddressCreate (vlc_object_t *obj, const char *group)
84 int fd = net_ConnectUDP (obj, group, IPPORT_SAP, 255);
88 sap_address_t *addr = malloc (sizeof (*addr));
95 strlcpy (addr->group, group, sizeof (addr->group));
97 addr->origlen = sizeof (addr->orig);
98 getsockname (fd, (struct sockaddr *)&addr->orig, &addr->origlen);
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;
106 if (vlc_clone (&addr->thread, RunThread, addr, VLC_THREAD_PRIORITY_LOW))
108 msg_Err (obj, "unable to spawn SAP announce thread");
116 static void AddressDestroy (sap_address_t *addr)
118 assert (addr->first == NULL);
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);
129 * main SAP handler thread
130 * \param p_this the SAP Handler object
134 static void *RunThread (void *self)
136 sap_address_t *addr = self;
138 vlc_mutex_lock (&addr->lock);
139 mutex_cleanup_push (&addr->lock);
143 session_descriptor_t *p_session;
146 while (addr->first == NULL)
147 vlc_cond_wait (&addr->wait, &addr->lock);
149 assert (addr->session_count > 0);
152 for (p_session = addr->first; p_session; p_session = p_session->next)
154 send (addr->fd, p_session->data, p_session->length, 0);
155 deadline += addr->interval * CLOCK_FREQ / addr->session_count;
157 if (vlc_cond_timedwait (&addr->wait, &addr->lock, deadline) == 0)
158 break; /* list may have changed! */
166 #undef sout_AnnounceRegisterSDP
168 * Registers a new session with the announce handler, using a pregenerated SDP
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
175 session_descriptor_t *
176 sout_AnnounceRegisterSDP (vlc_object_t *obj, const char *sdp,
180 char psz_addr[NI_MAXNUMERICHOST];
184 struct sockaddr_in in;
185 struct sockaddr_in6 in6;
187 socklen_t addrlen = 0;
188 struct addrinfo *res;
190 msg_Dbg (obj, "adding SAP session");
192 if (vlc_getaddrinfo (dst, 0, NULL, &res) == 0)
194 if (res->ai_addrlen <= sizeof (addr))
195 memcpy (&addr, res->ai_addr, res->ai_addrlen);
196 addrlen = res->ai_addrlen;
200 if (addrlen == 0 || addrlen > sizeof (addr))
202 msg_Err (obj, "No/invalid address specified for SAP announce" );
206 /* Determine SAP multicast address automatically */
207 switch (addr.a.sa_family)
209 #if defined (HAVE_INET_PTON) || defined (_WIN32)
212 /* See RFC3513 for list of valid IPv6 scopes */
213 struct in6_addr *a6 = &addr.in6.sin6_addr;
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;
221 /* Unicast IPv6 - assume global scope */
222 memcpy( a6->s6_addr, "\xff\x0e", 2 );
229 /* See RFC2365 for IPv4 scopes */
230 uint32_t ipv4 = addr.in.sin_addr.s_addr;
232 /* 224.0.0.0/24 => 224.0.0.255 */
233 if ((ipv4 & htonl (0xffffff00)) == htonl (0xe0000000))
234 ipv4 = htonl (0xe00000ff);
236 /* 239.255.0.0/16 => 239.255.255.255 */
237 if ((ipv4 & htonl (0xffff0000)) == htonl (0xefff0000))
238 ipv4 = htonl (0xefffffff);
240 /* 239.192.0.0/14 => 239.195.255.255 */
241 if ((ipv4 & htonl (0xfffc0000)) == htonl (0xefc00000))
242 ipv4 = htonl (0xefc3ffff);
244 if ((ipv4 & htonl (0xff000000)) == htonl (0xef000000))
247 /* other addresses => 224.2.127.254 */
248 ipv4 = htonl (0xe0027ffe);
252 msg_Err (obj, "Out-of-scope multicast address "
253 "not supported by SAP");
257 addr.in.sin_addr.s_addr = ipv4;
262 msg_Err (obj, "Address family %d not supported by SAP",
267 i = vlc_getnameinfo( &addr.a, addrlen,
268 psz_addr, sizeof( psz_addr ), NULL, NI_NUMERICHOST );
272 msg_Err (obj, "%s", gai_strerror (i));
276 /* Find/create SAP address thread */
277 sap_address_t *sap_addr;
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))
285 if (sap_addr == NULL)
287 sap_addr = AddressCreate (obj, psz_addr);
288 if (sap_addr == NULL)
290 vlc_mutex_unlock (&sap_mutex);
293 sap_addr->next = sap_addrs;
294 sap_addrs = sap_addr;
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);
302 switch (sap_addr->orig.ss_family)
316 /* XXX: Check for dupes */
317 length += strlen (sdp);
319 session_descriptor_t *session = malloc (sizeof (*session) + length);
320 if (unlikely(session == NULL))
322 vlc_mutex_unlock (&sap_addr->lock);
323 return NULL; /* NOTE: we should destroy the thread if left unused */
325 session->next = sap_addr->first;
326 sap_addr->first = session;
327 session->length = length;
329 /* Build the SAP Headers */
330 uint8_t *buf = session->data;
332 /* SAPv1, not encrypted, not compressed */
334 buf[1] = 0x00; /* No authentication length */
335 SetWBE(buf + 2, mdate()); /* ID hash */
338 switch (sap_addr->orig.ss_family)
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 */
353 const struct in_addr *a4 =
354 &((const struct sockaddr_in *)&sap_addr->orig)->sin_addr;
355 memcpy (buf + offset, a4, 4);
362 memcpy (buf + offset, "application/sdp", 16);
365 /* Build the final message */
366 strcpy((char *)buf + offset, sdp);
368 sap_addr->session_count++;
369 vlc_cond_signal (&sap_addr->wait);
370 vlc_mutex_unlock (&sap_addr->lock);
374 #undef sout_AnnounceUnRegister
376 * Unregisters an existing session
378 * \param obj a VLC object
379 * \param session the session descriptor
380 * \return VLC_SUCCESS
382 int sout_AnnounceUnRegister (vlc_object_t *obj, session_descriptor_t *session)
384 sap_address_t *addr, **paddr;
385 session_descriptor_t **psession;
387 msg_Dbg (obj, "removing SAP session");
388 vlc_mutex_lock (&sap_mutex);
393 assert (addr != NULL);
395 psession = &addr->first;
396 vlc_mutex_lock (&addr->lock);
397 while (*psession != NULL)
399 if (*psession == session)
401 psession = &(*psession)->next;
403 vlc_mutex_unlock (&addr->lock);
408 *psession = session->next;
410 if (addr->first == NULL)
411 /* Last session for this address -> unlink the address */
413 vlc_mutex_unlock (&sap_mutex);
415 if (addr->first == NULL)
417 /* Last session for this address -> unlink the address */
418 vlc_mutex_unlock (&addr->lock);
419 AddressDestroy (addr);
423 addr->session_count--;
424 vlc_cond_signal (&addr->wait);
425 vlc_mutex_unlock (&addr->lock);