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 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
33 #include <vlc_common.h>
35 #include <stdlib.h> /* free() */
36 #include <stdio.h> /* sprintf() */
41 #include <vlc_network.h>
43 #include "stream_output.h"
46 /* SAP is always on that port */
47 #define IPPORT_SAP 9875
49 /* A SAP session descriptor, enqueued in the SAP handler queue */
50 typedef struct sap_session_t
52 struct sap_session_t
*next
;
53 const session_descriptor_t
*p_sd
;
58 /* A SAP announce address. For each of these, we run the
59 * control flow algorithm */
60 typedef struct sap_address_t
62 struct sap_address_t
*next
;
68 char group
[NI_MAXNUMERICHOST
];
69 struct sockaddr_storage orig
;
74 unsigned session_count
;
78 /* The SAP handler, running in a separate thread */
87 #define SAP_MAX_BUFFER 65534
88 #define MIN_INTERVAL 2
89 #define MAX_INTERVAL 300
91 /*****************************************************************************
93 *****************************************************************************/
94 static void *RunThread (void *);
97 * Create the SAP handler
99 * \param p_announce a VLC object
100 * \return the newly created SAP handler or NULL on error
102 sap_handler_t
*SAP_Create (vlc_object_t
*p_announce
)
104 sap_handler_t
*p_sap
;
106 p_sap
= vlc_custom_create (p_announce
, sizeof (*p_sap
), "sap sender");
110 vlc_mutex_init (&p_sap
->lock
);
115 void SAP_Destroy (sap_handler_t
*p_sap
)
117 assert (p_sap
->first
== NULL
);
118 vlc_mutex_destroy (&p_sap
->lock
);
119 vlc_object_release (p_sap
);
122 static sap_address_t
*AddressCreate (vlc_object_t
*obj
, const char *group
)
124 int fd
= net_ConnectUDP (obj
, group
, IPPORT_SAP
, 255);
128 sap_address_t
*addr
= malloc (sizeof (*addr
));
135 strlcpy (addr
->group
, group
, sizeof (addr
->group
));
137 addr
->origlen
= sizeof (addr
->orig
);
138 getsockname (fd
, (struct sockaddr
*)&addr
->orig
, &addr
->origlen
);
140 addr
->interval
= var_CreateGetInteger (obj
, "sap-interval");
141 vlc_mutex_init (&addr
->lock
);
142 vlc_cond_init (&addr
->wait
);
143 addr
->session_count
= 0;
146 if (vlc_clone (&addr
->thread
, RunThread
, addr
, VLC_THREAD_PRIORITY_LOW
))
148 msg_Err (obj
, "unable to spawn SAP announce thread");
156 static void AddressDestroy (sap_address_t
*addr
)
158 assert (addr
->first
== NULL
);
160 vlc_cancel (addr
->thread
);
161 vlc_join (addr
->thread
, NULL
);
162 vlc_cond_destroy (&addr
->wait
);
163 vlc_mutex_destroy (&addr
->lock
);
164 net_Close (addr
->fd
);
169 * main SAP handler thread
170 * \param p_this the SAP Handler object
174 static void *RunThread (void *self
)
176 sap_address_t
*addr
= self
;
178 vlc_mutex_lock (&addr
->lock
);
179 mutex_cleanup_push (&addr
->lock
);
183 sap_session_t
*p_session
;
186 while (addr
->first
== NULL
)
187 vlc_cond_wait (&addr
->wait
, &addr
->lock
);
189 assert (addr
->session_count
> 0);
192 for (p_session
= addr
->first
; p_session
; p_session
= p_session
->next
)
194 send (addr
->fd
, p_session
->data
, p_session
->length
, 0);
195 deadline
+= addr
->interval
* CLOCK_FREQ
/ addr
->session_count
;
197 if (vlc_cond_timedwait (&addr
->wait
, &addr
->lock
, deadline
) == 0)
198 break; /* list may have changed! */
209 int SAP_Add (sap_handler_t
*p_sap
, session_descriptor_t
*p_session
)
212 char psz_addr
[NI_MAXNUMERICHOST
];
213 sap_session_t
*p_sap_session
;
218 struct sockaddr_in in
;
219 struct sockaddr_in6 in6
;
223 addrlen
= p_session
->addrlen
;
224 if ((addrlen
== 0) || (addrlen
> sizeof (addr
)))
226 msg_Err( p_sap
, "No/invalid address specified for SAP announce" );
230 /* Determine SAP multicast address automatically */
231 memcpy (&addr
, &p_session
->addr
, addrlen
);
233 switch (addr
.a
.sa_family
)
235 #if defined (HAVE_INET_PTON) || defined (_WIN32)
238 /* See RFC3513 for list of valid IPv6 scopes */
239 struct in6_addr
*a6
= &addr
.in6
.sin6_addr
;
241 memcpy( a6
->s6_addr
+ 2, "\x00\x00\x00\x00\x00\x00"
242 "\x00\x00\x00\x00\x00\x02\x7f\xfe", 14 );
243 if( IN6_IS_ADDR_MULTICAST( a6
) )
244 /* force flags to zero, preserve scope */
245 a6
->s6_addr
[1] &= 0xf;
247 /* Unicast IPv6 - assume global scope */
248 memcpy( a6
->s6_addr
, "\xff\x0e", 2 );
255 /* See RFC2365 for IPv4 scopes */
256 uint32_t ipv4
= addr
.in
.sin_addr
.s_addr
;
258 /* 224.0.0.0/24 => 224.0.0.255 */
259 if ((ipv4
& htonl (0xffffff00)) == htonl (0xe0000000))
260 ipv4
= htonl (0xe00000ff);
262 /* 239.255.0.0/16 => 239.255.255.255 */
263 if ((ipv4
& htonl (0xffff0000)) == htonl (0xefff0000))
264 ipv4
= htonl (0xefffffff);
266 /* 239.192.0.0/14 => 239.195.255.255 */
267 if ((ipv4
& htonl (0xfffc0000)) == htonl (0xefc00000))
268 ipv4
= htonl (0xefc3ffff);
270 if ((ipv4
& htonl (0xff000000)) == htonl (0xef000000))
273 /* other addresses => 224.2.127.254 */
274 ipv4
= htonl (0xe0027ffe);
278 msg_Err( p_sap
, "Out-of-scope multicast address "
279 "not supported by SAP" );
283 addr
.in
.sin_addr
.s_addr
= ipv4
;
288 msg_Err( p_sap
, "Address family %d not supported by SAP",
293 i
= vlc_getnameinfo( &addr
.a
, addrlen
,
294 psz_addr
, sizeof( psz_addr
), NULL
, NI_NUMERICHOST
);
298 msg_Err( p_sap
, "%s", gai_strerror( i
) );
302 /* Find/create SAP address thread */
303 msg_Dbg( p_sap
, "using SAP address: %s", psz_addr
);
305 vlc_mutex_lock (&p_sap
->lock
);
306 sap_address_t
*sap_addr
;
307 for (sap_addr
= p_sap
->first
; sap_addr
; sap_addr
= sap_addr
->next
)
308 if (!strcmp (psz_addr
, sap_addr
->group
))
311 if (sap_addr
== NULL
)
313 sap_addr
= AddressCreate (VLC_OBJECT(p_sap
), psz_addr
);
314 if (sap_addr
== NULL
)
316 vlc_mutex_unlock (&p_sap
->lock
);
319 sap_addr
->next
= p_sap
->first
;
320 p_sap
->first
= sap_addr
;
323 * NEVER take the global SAP lock when holding a SAP thread lock! */
324 vlc_mutex_lock (&sap_addr
->lock
);
325 vlc_mutex_unlock (&p_sap
->lock
);
327 memcpy (&p_session
->orig
, &sap_addr
->orig
, sap_addr
->origlen
);
328 p_session
->origlen
= sap_addr
->origlen
;
330 size_t headsize
= 20, length
;
331 switch (p_session
->orig
.ss_family
)
345 /* XXX: Check for dupes */
346 length
= headsize
+ strlen (p_session
->psz_sdp
);
347 p_sap_session
= malloc (sizeof (*p_sap_session
) + length
+ 1);
348 if (p_sap_session
== NULL
)
350 vlc_mutex_unlock (&sap_addr
->lock
);
351 return VLC_EGENERIC
; /* NOTE: we should destroy the thread if left unused */
353 p_sap_session
->next
= sap_addr
->first
;
354 sap_addr
->first
= p_sap_session
;
355 p_sap_session
->p_sd
= p_session
;
356 p_sap_session
->length
= length
;
358 /* Build the SAP Headers */
359 uint8_t *psz_head
= p_sap_session
->data
;
361 /* SAPv1, not encrypted, not compressed */
363 psz_head
[1] = 0x00; /* No authentication length */
366 psz_head
[2] = i_hash
>> 8; /* Msg id hash */
367 psz_head
[3] = i_hash
; /* Msg id hash 2 */
370 switch (p_session
->orig
.ss_family
)
375 struct in6_addr
*a6
=
376 &((struct sockaddr_in6
*)&p_session
->orig
)->sin6_addr
;
377 memcpy (psz_head
+ headsize
, a6
, 16);
378 psz_head
[0] |= 0x10; /* IPv6 flag */
386 (((struct sockaddr_in
*)&p_session
->orig
)->sin_addr
.s_addr
);
387 memcpy (psz_head
+ headsize
, &ipv4
, 4);
394 memcpy (psz_head
+ headsize
, "application/sdp", 16);
397 /* Build the final message */
398 strcpy( (char *)psz_head
+ headsize
, p_session
->psz_sdp
);
400 sap_addr
->session_count
++;
401 vlc_cond_signal (&sap_addr
->wait
);
402 vlc_mutex_unlock (&sap_addr
->lock
);
407 * Remove a SAP Announce
409 void SAP_Del (sap_handler_t
*p_sap
, const session_descriptor_t
*p_session
)
411 vlc_mutex_lock (&p_sap
->lock
);
413 /* TODO: give a handle back in SAP_Add, and use that... */
414 sap_address_t
*addr
, **paddr
;
415 sap_session_t
*session
, **psession
;
417 paddr
= &p_sap
->first
;
418 for (addr
= p_sap
->first
; addr
; addr
= addr
->next
)
420 psession
= &addr
->first
;
421 vlc_mutex_lock (&addr
->lock
);
422 for (session
= addr
->first
; session
; session
= session
->next
)
424 if (session
->p_sd
== p_session
)
426 psession
= &session
->next
;
428 vlc_mutex_unlock (&addr
->lock
);
434 *psession
= session
->next
;
436 if (addr
->first
== NULL
)
437 /* Last session for this address -> unlink the address */
439 vlc_mutex_unlock (&p_sap
->lock
);
441 if (addr
->first
== NULL
)
443 /* Last session for this address -> unlink the address */
444 vlc_mutex_unlock (&addr
->lock
);
445 AddressDestroy (addr
);
449 addr
->session_count
--;
450 vlc_cond_signal (&addr
->wait
);
451 vlc_mutex_unlock (&addr
->lock
);