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 <stdnoreturn.h>
32 #include <stdlib.h> /* free() */
33 #include <stdio.h> /* sprintf() */
38 #include <vlc_network.h>
39 #include <vlc_memstream.h>
41 #include "stream_output.h"
44 /* SAP is always on that port */
45 #define IPPORT_SAP 9875
47 /* A SAP session descriptor, enqueued in the SAP handler queue */
48 struct session_descriptor_t
50 struct session_descriptor_t
*next
;
55 /* A SAP announce address. For each of these, we run the
56 * control flow algorithm */
57 typedef struct sap_address_t
59 struct sap_address_t
*next
;
65 char group
[NI_MAXNUMERICHOST
];
66 struct sockaddr_storage orig
;
71 unsigned session_count
;
72 session_descriptor_t
*first
;
75 static sap_address_t
*sap_addrs
= NULL
;
76 static vlc_mutex_t sap_mutex
= VLC_STATIC_MUTEX
;
78 #define SAP_MAX_BUFFER 65534
79 #define MIN_INTERVAL 2
80 #define MAX_INTERVAL 300
82 static void *RunThread (void *);
84 static sap_address_t
*AddressCreate (vlc_object_t
*obj
, const char *group
)
86 int fd
= net_ConnectUDP (obj
, group
, IPPORT_SAP
, 255);
90 sap_address_t
*addr
= malloc (sizeof (*addr
));
97 strlcpy (addr
->group
, group
, sizeof (addr
->group
));
99 addr
->origlen
= sizeof (addr
->orig
);
100 getsockname (fd
, (struct sockaddr
*)&addr
->orig
, &addr
->origlen
);
102 addr
->interval
= var_CreateGetInteger (obj
, "sap-interval");
103 vlc_mutex_init (&addr
->lock
);
104 vlc_cond_init (&addr
->wait
);
105 addr
->session_count
= 0;
108 if (vlc_clone (&addr
->thread
, RunThread
, addr
, VLC_THREAD_PRIORITY_LOW
))
110 msg_Err (obj
, "unable to spawn SAP announce thread");
118 static void AddressDestroy (sap_address_t
*addr
)
120 assert (addr
->first
== NULL
);
122 vlc_cancel (addr
->thread
);
123 vlc_join (addr
->thread
, NULL
);
124 vlc_cond_destroy (&addr
->wait
);
125 vlc_mutex_destroy (&addr
->lock
);
126 net_Close (addr
->fd
);
131 * main SAP handler thread
132 * \param p_this the SAP Handler object
135 noreturn
static void *RunThread (void *self
)
137 sap_address_t
*addr
= self
;
139 vlc_mutex_lock (&addr
->lock
);
140 mutex_cleanup_push (&addr
->lock
);
144 session_descriptor_t
*p_session
;
147 while (addr
->first
== NULL
)
148 vlc_cond_wait (&addr
->wait
, &addr
->lock
);
150 assert (addr
->session_count
> 0);
153 for (p_session
= addr
->first
; p_session
; p_session
= p_session
->next
)
155 send (addr
->fd
, p_session
->data
, p_session
->length
, 0);
156 deadline
+= addr
->interval
* CLOCK_FREQ
/ addr
->session_count
;
158 if (vlc_cond_timedwait (&addr
->wait
, &addr
->lock
, deadline
) == 0)
159 break; /* list may have changed! */
164 vlc_assert_unreachable ();
167 #undef sout_AnnounceRegisterSDP
169 * Registers a new session with the announce handler, using a pregenerated SDP
171 * \param obj a VLC object
172 * \param sdp the SDP to register
173 * \param dst session address (needed for SAP address auto detection)
174 * \return the new session descriptor structure
176 session_descriptor_t
*
177 sout_AnnounceRegisterSDP (vlc_object_t
*obj
, const char *sdp
,
181 char psz_addr
[NI_MAXNUMERICHOST
];
185 struct sockaddr_in in
;
186 struct sockaddr_in6 in6
;
188 socklen_t addrlen
= 0;
189 struct addrinfo
*res
;
191 msg_Dbg (obj
, "adding SAP session");
193 if (vlc_getaddrinfo (dst
, 0, NULL
, &res
) == 0)
195 if (res
->ai_addrlen
<= sizeof (addr
))
196 memcpy (&addr
, res
->ai_addr
, res
->ai_addrlen
);
197 addrlen
= res
->ai_addrlen
;
201 if (addrlen
== 0 || addrlen
> sizeof (addr
))
203 msg_Err (obj
, "No/invalid address specified for SAP announce" );
207 /* Determine SAP multicast address automatically */
208 switch (addr
.a
.sa_family
)
210 #if defined (HAVE_INET_PTON) || defined (_WIN32)
213 /* See RFC3513 for list of valid IPv6 scopes */
214 struct in6_addr
*a6
= &addr
.in6
.sin6_addr
;
216 memcpy( a6
->s6_addr
+ 2, "\x00\x00\x00\x00\x00\x00"
217 "\x00\x00\x00\x00\x00\x02\x7f\xfe", 14 );
218 if( IN6_IS_ADDR_MULTICAST( a6
) )
219 /* force flags to zero, preserve scope */
220 a6
->s6_addr
[1] &= 0xf;
222 /* Unicast IPv6 - assume global scope */
223 memcpy( a6
->s6_addr
, "\xff\x0e", 2 );
230 /* See RFC2365 for IPv4 scopes */
231 uint32_t ipv4
= addr
.in
.sin_addr
.s_addr
;
233 /* 224.0.0.0/24 => 224.0.0.255 */
234 if ((ipv4
& htonl (0xffffff00)) == htonl (0xe0000000))
235 ipv4
= htonl (0xe00000ff);
237 /* 239.255.0.0/16 => 239.255.255.255 */
238 if ((ipv4
& htonl (0xffff0000)) == htonl (0xefff0000))
239 ipv4
= htonl (0xefffffff);
241 /* 239.192.0.0/14 => 239.195.255.255 */
242 if ((ipv4
& htonl (0xfffc0000)) == htonl (0xefc00000))
243 ipv4
= htonl (0xefc3ffff);
245 if ((ipv4
& htonl (0xff000000)) == htonl (0xef000000))
248 /* other addresses => 224.2.127.254 */
249 ipv4
= htonl (0xe0027ffe);
253 msg_Err (obj
, "Out-of-scope multicast address "
254 "not supported by SAP");
258 addr
.in
.sin_addr
.s_addr
= ipv4
;
263 msg_Err (obj
, "Address family %d not supported by SAP",
268 i
= vlc_getnameinfo( &addr
.a
, addrlen
,
269 psz_addr
, sizeof( psz_addr
), NULL
, NI_NUMERICHOST
);
273 msg_Err (obj
, "%s", gai_strerror (i
));
277 /* Find/create SAP address thread */
278 sap_address_t
*sap_addr
;
280 msg_Dbg (obj
, "using SAP address: %s", psz_addr
);
281 vlc_mutex_lock (&sap_mutex
);
282 for (sap_addr
= sap_addrs
; sap_addr
; sap_addr
= sap_addr
->next
)
283 if (!strcmp (psz_addr
, sap_addr
->group
))
286 if (sap_addr
== NULL
)
288 sap_addr
= AddressCreate (obj
, psz_addr
);
289 if (sap_addr
== NULL
)
291 vlc_mutex_unlock (&sap_mutex
);
294 sap_addr
->next
= sap_addrs
;
295 sap_addrs
= sap_addr
;
298 * NEVER take the global SAP lock when holding a SAP thread lock! */
299 vlc_mutex_lock (&sap_addr
->lock
);
300 vlc_mutex_unlock (&sap_mutex
);
302 session_descriptor_t
*session
= malloc(sizeof (*session
));
303 if (unlikely(session
== NULL
))
304 goto out
; /* NOTE: we should destroy the thread if left unused */
306 session
->next
= sap_addr
->first
;
308 /* Build the SAP Headers */
309 struct vlc_memstream stream
;
310 vlc_memstream_open(&stream
);
312 /* SAPv1, not encrypted, not compressed */
313 uint8_t flags
= 0x20;
315 if (sap_addr
->orig
.ss_family
== AF_INET6
)
318 vlc_memstream_putc(&stream
, flags
);
319 vlc_memstream_putc(&stream
, 0x00); /* No authentication length */
320 vlc_memstream_write(&stream
, &(uint16_t){ mdate() }, 2); /* ID hash */
322 switch (sap_addr
->orig
.ss_family
)
327 const struct in6_addr
*a6
=
328 &((const struct sockaddr_in6
*)&sap_addr
->orig
)->sin6_addr
;
329 vlc_memstream_write(&stream
, &a6
, 16);
335 const struct in_addr
*a4
=
336 &((const struct sockaddr_in
*)&sap_addr
->orig
)->sin_addr
;
337 vlc_memstream_write(&stream
, &a4
, 4);
341 vlc_assert_unreachable ();
344 vlc_memstream_puts(&stream
, "application/sdp");
345 vlc_memstream_putc(&stream
, '\0');
347 /* Build the final message */
348 vlc_memstream_puts(&stream
, sdp
);
350 if (vlc_memstream_close(&stream
))
357 session
->data
= stream
.ptr
;
358 session
->length
= stream
.length
;
359 sap_addr
->first
= session
;
360 sap_addr
->session_count
++;
361 vlc_cond_signal (&sap_addr
->wait
);
363 vlc_mutex_unlock (&sap_addr
->lock
);
367 #undef sout_AnnounceUnRegister
369 * Unregisters an existing session
371 * \param obj a VLC object
372 * \param session the session descriptor
374 void sout_AnnounceUnRegister (vlc_object_t
*obj
, session_descriptor_t
*session
)
376 sap_address_t
*addr
, **paddr
;
377 session_descriptor_t
**psession
;
379 msg_Dbg (obj
, "removing SAP session");
380 vlc_mutex_lock (&sap_mutex
);
385 assert (addr
!= NULL
);
387 psession
= &addr
->first
;
388 vlc_mutex_lock (&addr
->lock
);
389 while (*psession
!= NULL
)
391 if (*psession
== session
)
393 psession
= &(*psession
)->next
;
395 vlc_mutex_unlock (&addr
->lock
);
400 *psession
= session
->next
;
402 if (addr
->first
== NULL
)
403 /* Last session for this address -> unlink the address */
405 vlc_mutex_unlock (&sap_mutex
);
407 if (addr
->first
== NULL
)
409 /* Last session for this address -> unlink the address */
410 vlc_mutex_unlock (&addr
->lock
);
411 AddressDestroy (addr
);
415 addr
->session_count
--;
416 vlc_cond_signal (&addr
->wait
);
417 vlc_mutex_unlock (&addr
->lock
);