1 /*****************************************************************************
2 * sap.c : SAP interface module
3 *****************************************************************************
4 * Copyright (C) 2004-2005 the VideoLAN team
5 * Copyright © 2007 Rémi Denis-Courmont
8 * Authors: Clément Stenac <zorglub@videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
37 #include <vlc_demux.h>
38 #include <vlc_services_discovery.h>
40 #include <vlc_network.h>
41 #include <vlc_charset.h>
58 /************************************************************************
59 * Macros and definitions
60 ************************************************************************/
62 #define MAX_LINE_LENGTH 256
64 /* SAP is always on that port */
66 /* Global-scope SAP address */
67 #define SAP_V4_GLOBAL_ADDRESS "224.2.127.254"
68 /* Organization-local SAP address */
69 #define SAP_V4_ORG_ADDRESS "239.195.255.255"
70 /* Local (smallest non-link-local scope) SAP address */
71 #define SAP_V4_LOCAL_ADDRESS "239.255.255.255"
72 /* Link-local SAP address */
73 #define SAP_V4_LINK_ADDRESS "224.0.0.255"
76 /*****************************************************************************
78 *****************************************************************************/
79 #define SAP_ADDR_TEXT N_( "SAP multicast address" )
80 #define SAP_ADDR_LONGTEXT N_( "The SAP module normally chooses itself the " \
81 "right addresses to listen to. However, you " \
82 "can specify a specific address." )
83 #define SAP_IPV4_TEXT N_( "IPv4 SAP" )
84 #define SAP_IPV4_LONGTEXT N_( \
85 "Listen to IPv4 announcements on the standard addresses." )
86 #define SAP_IPV6_TEXT N_( "IPv6 SAP" )
87 #define SAP_IPV6_LONGTEXT N_( \
88 "Listen to IPv6 announcements on the standard addresses." )
89 #define SAP_SCOPE_TEXT N_( "IPv6 SAP scope" )
90 #define SAP_SCOPE_LONGTEXT N_( \
91 "Scope for IPv6 announcements (default is 8)." )
92 #define SAP_TIMEOUT_TEXT N_( "SAP timeout (seconds)" )
93 #define SAP_TIMEOUT_LONGTEXT N_( \
94 "Delay after which SAP items get deleted if no new announcement " \
96 #define SAP_PARSE_TEXT N_( "Try to parse the announce" )
97 #define SAP_PARSE_LONGTEXT N_( \
98 "This enables actual parsing of the announces by the SAP module. " \
99 "Otherwise, all announcements are parsed by the \"live555\" " \
100 "(RTP/RTSP) module." )
101 #define SAP_STRICT_TEXT N_( "SAP Strict mode" )
102 #define SAP_STRICT_LONGTEXT N_( \
103 "When this is set, the SAP parser will discard some non-compliant " \
105 #define SAP_CACHE_TEXT N_("Use SAP cache")
106 #define SAP_CACHE_LONGTEXT N_( \
107 "This enables a SAP caching mechanism. " \
108 "This will result in lower SAP startup time, but you could end up " \
109 "with items corresponding to legacy streams." )
112 static int Open ( vlc_object_t
* );
113 static void Close( vlc_object_t
* );
114 static int OpenDemux ( vlc_object_t
* );
115 static void CloseDemux ( vlc_object_t
* );
117 VLC_SD_PROBE_HELPER("sap", "Network streams (SAP)", SD_CAT_LAN
)
120 set_shortname( N_("SAP"))
121 set_description( N_("Network streams (SAP)") )
122 set_category( CAT_PLAYLIST
)
123 set_subcategory( SUBCAT_PLAYLIST_SD
)
125 add_string( "sap-addr", NULL
,
126 SAP_ADDR_TEXT
, SAP_ADDR_LONGTEXT
, true )
127 add_bool( "sap-ipv4", true, NULL
,
128 SAP_IPV4_TEXT
,SAP_IPV4_LONGTEXT
, true )
129 add_bool( "sap-ipv6", true, NULL
,
130 SAP_IPV6_TEXT
, SAP_IPV6_LONGTEXT
, true )
131 add_integer( "sap-timeout", 1800,
132 SAP_TIMEOUT_TEXT
, SAP_TIMEOUT_LONGTEXT
, true )
133 add_bool( "sap-parse", true, NULL
,
134 SAP_PARSE_TEXT
,SAP_PARSE_LONGTEXT
, true )
135 add_bool( "sap-strict", false, NULL
,
136 SAP_STRICT_TEXT
,SAP_STRICT_LONGTEXT
, true )
138 add_bool( "sap-cache", false, NULL
,
139 SAP_CACHE_TEXT
,SAP_CACHE_LONGTEXT
, true )
141 add_obsolete_bool( "sap-timeshift" ) /* Redumdant since 1.0.0 */
143 set_capability( "services_discovery", 0 )
144 set_callbacks( Open
, Close
)
146 VLC_SD_PROBE_SUBMODULE
149 set_description( N_("SDP Descriptions parser") )
150 add_shortcut( "sdp" )
151 set_capability( "demux", 51 )
152 set_callbacks( OpenDemux
, CloseDemux
)
156 /*****************************************************************************
158 *****************************************************************************/
160 typedef struct sdp_t sdp_t
;
161 typedef struct attribute_t attribute_t
;
162 typedef struct sap_announce_t sap_announce_t
;
167 struct sdp_t
*parent
;
169 struct sockaddr_storage addr
;
173 attribute_t
**pp_attributes
;
177 /* The structure that contains sdp information */
185 uint64_t session_version
;
186 unsigned orig_ip_version
;
187 char orig_host
[1024];
190 char *psz_sessionname
;
198 /* a= global attributes */
200 attribute_t
**pp_attributes
;
202 /* medias (well, we only support one atm) */
204 struct sdp_media_t
*mediav
;
213 struct sap_announce_t
217 uint8_t i_period_trust
;
220 uint32_t i_source
[4];
222 /* SAP annnounces must only contain one SDP */
225 input_item_t
* p_item
;
228 struct services_discovery_sys_t
232 /* Socket descriptors */
236 /* Table of announces */
238 struct sap_announce_t
**pp_announces
;
252 /*****************************************************************************
254 *****************************************************************************/
258 static int Demux( demux_t
*p_demux
);
259 static int Control( demux_t
*, int, va_list );
260 static void *Run ( void *p_sd
);
262 /* Main parsing functions */
263 static int ParseConnection( vlc_object_t
*p_obj
, sdp_t
*p_sdp
);
264 static int ParseSAP( services_discovery_t
*p_sd
, const uint8_t *p_buffer
, size_t i_read
);
265 static sdp_t
*ParseSDP (vlc_object_t
*p_sd
, const char *psz_sdp
);
266 static sap_announce_t
*CreateAnnounce( services_discovery_t
*, uint16_t, sdp_t
* );
267 static int RemoveAnnounce( services_discovery_t
*p_sd
, sap_announce_t
*p_announce
);
269 /* Helper functions */
270 static inline attribute_t
*MakeAttribute (const char *str
);
271 static const char *GetAttribute (attribute_t
**tab
, unsigned n
, const char *name
);
272 static inline void FreeAttribute (attribute_t
*a
);
273 static const char *FindAttribute (const sdp_t
*sdp
, unsigned media
,
276 static bool IsSameSession( sdp_t
*p_sdp1
, sdp_t
*p_sdp2
);
277 static int InitSocket( services_discovery_t
*p_sd
, const char *psz_address
, int i_port
);
278 static int Decompress( const unsigned char *psz_src
, unsigned char **_dst
, int i_len
);
279 static void FreeSDP( sdp_t
*p_sdp
);
281 static inline int min_int( int a
, int b
)
283 return a
> b
? b
: a
;
286 static bool IsWellKnownPayload (int type
)
289 { /* Should be in sync with modules/demux/rtp.c */
290 case 0: /* PCMU/8000 */
292 case 8: /* PCMA/8000 */
293 case 10: /* L16/44100/2 */
294 case 11: /* L16/44100 */
296 case 14: /* MPA/90000 */
297 case 32: /* MPV/90000 */
298 case 33: /* MP2/90000 */
304 /*****************************************************************************
305 * Open: initialize and create stuff
306 *****************************************************************************/
307 static int Open( vlc_object_t
*p_this
)
309 services_discovery_t
*p_sd
= ( services_discovery_t
* )p_this
;
310 services_discovery_sys_t
*p_sys
= (services_discovery_sys_t
*)
311 malloc( sizeof( services_discovery_sys_t
) );
315 p_sys
->i_timeout
= var_CreateGetInteger( p_sd
, "sap-timeout" );
322 p_sys
->b_strict
= var_CreateGetBool( p_sd
, "sap-strict");
323 p_sys
->b_parse
= var_CreateGetBool( p_sd
, "sap-parse" );
326 if( var_CreateGetBool( p_sd
, "sap-cache" ) )
332 p_sys
->i_announces
= 0;
333 p_sys
->pp_announces
= NULL
;
334 /* TODO: create sockets here, and fix racy sockets table */
335 if (vlc_clone (&p_sys
->thread
, Run
, p_sd
, VLC_THREAD_PRIORITY_LOW
))
344 /*****************************************************************************
345 * OpenDemux: initialize and create stuff
346 *****************************************************************************/
347 static int OpenDemux( vlc_object_t
*p_this
)
349 demux_t
*p_demux
= (demux_t
*)p_this
;
350 const uint8_t *p_peek
;
351 char *psz_sdp
= NULL
;
353 int errval
= VLC_EGENERIC
;
356 if( !var_CreateGetBool( p_demux
, "sap-parse" ) )
358 /* We want livedotcom module to parse this SDP file */
362 assert( p_demux
->s
); /* this is NOT an access_demux */
365 if( stream_Peek( p_demux
->s
, &p_peek
, 7 ) < 7 )
368 if( memcmp( p_peek
, "v=0\r\no=", 7 ) && memcmp( p_peek
, "v=0\no=", 6 ) )
371 /* Gather the complete sdp file */
372 for( i_len
= 0, psz_sdp
= NULL
; i_len
< 65536; )
374 const int i_read_max
= 1024;
375 char *psz_sdp_new
= realloc( psz_sdp
, i_len
+ i_read_max
);
377 if( psz_sdp_new
== NULL
)
382 psz_sdp
= psz_sdp_new
;
384 i_read
= stream_Read( p_demux
->s
, &psz_sdp
[i_len
], i_read_max
);
385 if( (int)i_read
< 0 )
387 msg_Err( p_demux
, "cannot read SDP" );
392 psz_sdp
[i_len
] = '\0';
394 if( (int)i_read
< i_read_max
)
398 p_sdp
= ParseSDP( VLC_OBJECT(p_demux
), psz_sdp
);
402 msg_Warn( p_demux
, "invalid SDP");
406 if( ParseConnection( VLC_OBJECT( p_demux
), p_sdp
) )
408 p_sdp
->psz_uri
= NULL
;
410 if (!IsWellKnownPayload (p_sdp
->i_media_type
))
412 if( p_sdp
->psz_uri
== NULL
) goto error
;
414 p_demux
->p_sys
= (demux_sys_t
*)malloc( sizeof(demux_sys_t
) );
415 if( unlikely( !p_demux
->p_sys
) )
417 p_demux
->p_sys
->p_sdp
= p_sdp
;
418 p_demux
->pf_control
= Control
;
419 p_demux
->pf_demux
= Demux
;
426 if( p_sdp
) FreeSDP( p_sdp
); p_sdp
= NULL
;
427 stream_Seek( p_demux
->s
, 0 );
431 /*****************************************************************************
433 *****************************************************************************/
434 static void Close( vlc_object_t
*p_this
)
436 services_discovery_t
*p_sd
= ( services_discovery_t
* )p_this
;
437 services_discovery_sys_t
*p_sys
= p_sd
->p_sys
;
440 vlc_cancel (p_sys
->thread
);
441 vlc_join (p_sys
->thread
, NULL
);
443 for( i
= p_sys
->i_fd
-1 ; i
>= 0 ; i
-- )
445 net_Close( p_sys
->pi_fd
[i
] );
447 FREENULL( p_sys
->pi_fd
);
450 if( var_InheritBool( p_sd
, "sap-cache" ) )
456 for( i
= p_sys
->i_announces
- 1; i
>= 0; i
-- )
458 RemoveAnnounce( p_sd
, p_sys
->pp_announces
[i
] );
460 FREENULL( p_sys
->pp_announces
);
465 /*****************************************************************************
466 * CloseDemux: Close the demuxer
467 *****************************************************************************/
468 static void CloseDemux( vlc_object_t
*p_this
)
470 demux_t
*p_demux
= (demux_t
*)p_this
;
472 if( p_demux
->p_sys
->p_sdp
)
473 FreeSDP( p_demux
->p_sys
->p_sdp
);
474 free( p_demux
->p_sys
);
477 /*****************************************************************************
478 * Run: main SAP thread
479 *****************************************************************************
480 * Listens to SAP packets, and sends them to packet_handle
481 *****************************************************************************/
482 #define MAX_SAP_BUFFER 5000
484 static void *Run( void *data
)
486 services_discovery_t
*p_sd
= data
;
490 int canc
= vlc_savecancel ();
492 /* Braindead Winsock DNS resolver will get stuck over 2 seconds per failed
493 * DNS queries, even if the DNS server returns an error with milliseconds.
494 * You don't want to know why the bug (as of XP SP2) wasn't fixed since
495 * Winsock 1.1 from Windows 95, if not Windows 3.1.
496 * Anyway, to avoid a 30 seconds delay for failed IPv6 socket creation,
497 * we have to open sockets in Run() rather than Open(). */
498 if( var_CreateGetBool( p_sd
, "sap-ipv4" ) )
500 InitSocket( p_sd
, SAP_V4_GLOBAL_ADDRESS
, SAP_PORT
);
501 InitSocket( p_sd
, SAP_V4_ORG_ADDRESS
, SAP_PORT
);
502 InitSocket( p_sd
, SAP_V4_LOCAL_ADDRESS
, SAP_PORT
);
503 InitSocket( p_sd
, SAP_V4_LINK_ADDRESS
, SAP_PORT
);
505 if( var_CreateGetBool( p_sd
, "sap-ipv6" ) )
507 char psz_address
[NI_MAXNUMERICHOST
] = "ff02::2:7ffe%";
510 struct if_nameindex
*l
= if_nameindex ();
513 char *ptr
= strchr (psz_address
, '%') + 1;
514 for (unsigned i
= 0; l
[i
].if_index
; i
++)
516 strcpy (ptr
, l
[i
].if_name
);
517 InitSocket (p_sd
, psz_address
, SAP_PORT
);
519 if_freenameindex (l
);
522 /* this is the Winsock2 equivalant of SIOCGIFCONF on BSD stacks,
523 which if_nameindex uses internally anyway */
525 // first create a dummy socket to pin down the protocol family
526 SOCKET s
= socket(PF_INET6
, SOCK_DGRAM
, IPPROTO_UDP
);
527 if( s
!= INVALID_SOCKET
)
529 INTERFACE_INFO ifaces
[10]; // Assume there will be no more than 10 IP interfaces
530 size_t len
= sizeof(ifaces
);
532 if( SOCKET_ERROR
!= WSAIoctl(s
, SIO_GET_INTERFACE_LIST
, NULL
, 0, &ifaces
, len
, &len
, NULL
, NULL
) )
534 unsigned ifcount
= len
/sizeof(INTERFACE_INFO
);
535 char *ptr
= strchr (psz_address
, '%') + 1;
536 for(unsigned i
= 1; i
<=ifcount
; ++i
)
538 // append link-local zone identifier
539 sprintf(ptr
, "%d", i
);
545 *strchr (psz_address
, '%') = '\0';
547 static const char ipv6_scopes
[] = "1456789ABCDE";
548 for (const char *c_scope
= ipv6_scopes
; *c_scope
; c_scope
++)
550 psz_address
[3] = *c_scope
;
551 InitSocket( p_sd
, psz_address
, SAP_PORT
);
555 psz_addr
= var_CreateGetString( p_sd
, "sap-addr" );
556 if( psz_addr
&& *psz_addr
)
557 InitSocket( p_sd
, psz_addr
, SAP_PORT
);
560 if( p_sd
->p_sys
->i_fd
== 0 )
562 msg_Err( p_sd
, "unable to listen on any address" );
566 /* read SAP packets */
569 vlc_restorecancel (canc
);
570 unsigned n
= p_sd
->p_sys
->i_fd
;
571 struct pollfd ufd
[n
];
573 for (unsigned i
= 0; i
< n
; i
++)
575 ufd
[i
].fd
= p_sd
->p_sys
->pi_fd
[i
];
576 ufd
[i
].events
= POLLIN
;
580 int val
= poll (ufd
, n
, timeout
);
581 canc
= vlc_savecancel ();
584 for (unsigned i
= 0; i
< n
; i
++)
588 uint8_t p_buffer
[MAX_SAP_BUFFER
+1];
591 i_read
= net_Read (p_sd
, ufd
[i
].fd
, NULL
, p_buffer
,
592 MAX_SAP_BUFFER
, false);
594 msg_Warn (p_sd
, "receive error: %m");
597 /* Parse the packet */
598 p_buffer
[i_read
] = '\0';
599 ParseSAP (p_sd
, p_buffer
, i_read
);
605 mtime_t now
= mdate();
607 /* A 1 hour timeout correspong to the RFC Implicit timeout.
608 * This timeout is tuned in the following loop. */
609 timeout
= 1000 * 60 * 60;
611 /* Check for items that need deletion */
612 for( i
= 0; i
< p_sd
->p_sys
->i_announces
; i
++ )
614 mtime_t i_timeout
= ( mtime_t
) 1000000 * p_sd
->p_sys
->i_timeout
;
615 sap_announce_t
* p_announce
= p_sd
->p_sys
->pp_announces
[i
];
616 mtime_t i_last_period
= now
- p_announce
->i_last
;
618 /* Remove the annoucement, if the last announcement was 1 hour ago
619 * or if the last packet emitted was 3 times the average time
620 * between two packets */
621 if( ( p_announce
->i_period_trust
> 5 && i_last_period
> 3 * p_announce
->i_period
) ||
622 i_last_period
> i_timeout
)
624 RemoveAnnounce( p_sd
, p_announce
);
628 /* Compute next timeout */
629 if( p_announce
->i_period_trust
> 5 )
630 timeout
= min_int((3 * p_announce
->i_period
- i_last_period
) / 1000, timeout
);
631 timeout
= min_int((i_timeout
- i_last_period
)/1000, timeout
);
635 if( !p_sd
->p_sys
->i_announces
)
636 timeout
= -1; /* We can safely poll indefinitly. */
637 else if( timeout
< 200 )
638 timeout
= 200; /* Don't wakeup too fast. */
643 /**********************************************************************
644 * Demux: reads and demuxes data packets
645 * Return -1 if error, 0 if EOF, 1 else
646 **********************************************************************/
647 static int Demux( demux_t
*p_demux
)
649 sdp_t
*p_sdp
= p_demux
->p_sys
->p_sdp
;
650 input_thread_t
*p_input
;
651 input_item_t
*p_parent_input
;
653 p_input
= demux_GetParentInput( p_demux
);
657 msg_Err( p_demux
, "parent input could not be found" );
661 /* This item hasn't been held by input_GetItem
662 * don't release it */
663 p_parent_input
= input_GetItem( p_input
);
665 input_item_SetURI( p_parent_input
, p_sdp
->psz_uri
);
666 input_item_SetName( p_parent_input
, p_sdp
->psz_sessionname
);
667 if( p_sdp
->rtcp_port
)
670 if( asprintf( &rtcp
, ":rtcp-port=%u", p_sdp
->rtcp_port
) != -1 )
672 input_item_AddOption( p_parent_input
, rtcp
, VLC_INPUT_OPTION_TRUSTED
);
677 vlc_mutex_lock( &p_parent_input
->lock
);
679 p_parent_input
->i_type
= ITEM_TYPE_NET
;
681 vlc_mutex_unlock( &p_parent_input
->lock
);
682 vlc_object_release( p_input
);
686 static int Control( demux_t
*p_demux
, int i_query
, va_list args
)
688 VLC_UNUSED(p_demux
); VLC_UNUSED(i_query
); VLC_UNUSED(args
);
692 /**************************************************************
694 **************************************************************/
696 /* i_read is at least > 6 */
697 static int ParseSAP( services_discovery_t
*p_sd
, const uint8_t *buf
,
702 const uint8_t *end
= buf
+ len
;
705 assert (buf
[len
] == '\0');
710 uint8_t flags
= buf
[0];
712 /* First, check the sap announce is correct */
713 if ((flags
>> 5) != 1)
716 bool b_ipv6
= (flags
& 0x10) != 0;
717 bool b_need_delete
= (flags
& 0x04) != 0;
721 msg_Dbg( p_sd
, "encrypted packet, unsupported" );
725 bool b_compressed
= (flags
& 0x01) != 0;
727 uint16_t i_hash
= U16_AT (buf
+ 2);
729 if( p_sd
->p_sys
->b_strict
&& i_hash
== 0 )
731 msg_Dbg( p_sd
, "strict mode, discarding announce with null id hash");
735 // Skips source address and auth data
736 buf
+= 4 + (b_ipv6
? 16 : 4) + buf
[1];
740 uint8_t *decomp
= NULL
;
743 int newsize
= Decompress (buf
, &decomp
, end
- buf
);
746 msg_Dbg( p_sd
, "decompression of SAP packet failed" );
750 decomp
= realloc (decomp
, newsize
+ 1);
751 decomp
[newsize
] = '\0';
752 psz_sdp
= (const char *)decomp
;
757 psz_sdp
= (const char *)buf
;
761 /* len is a strlen here here. both buf and decomp are len+1 where the 1 should be a \0 */
762 assert( psz_sdp
[len
] == '\0');
764 /* Skip payload type */
765 /* SAPv1 has implicit "application/sdp" payload type: first line is v=0 */
766 if (strncmp (psz_sdp
, "v=0", 3))
768 size_t clen
= strlen (psz_sdp
) + 1;
770 if (strcmp (psz_sdp
, "application/sdp"))
772 msg_Dbg (p_sd
, "unsupported content type: %s", psz_sdp
);
776 // skips content type
785 p_sdp
= ParseSDP( VLC_OBJECT(p_sd
), psz_sdp
);
790 p_sdp
->psz_sdp
= psz_sdp
;
792 /* Decide whether we should add a playlist item for this SDP */
793 /* Parse connection information (c= & m= ) */
794 if( ParseConnection( VLC_OBJECT(p_sd
), p_sdp
) )
795 p_sdp
->psz_uri
= NULL
;
797 /* Multi-media or no-parse -> pass to LIVE.COM */
798 if( !IsWellKnownPayload( p_sdp
->i_media_type
) || !p_sd
->p_sys
->b_parse
)
800 free( p_sdp
->psz_uri
);
801 if (asprintf( &p_sdp
->psz_uri
, "sdp://%s", p_sdp
->psz_sdp
) == -1)
802 p_sdp
->psz_uri
= NULL
;
805 if( p_sdp
->psz_uri
== NULL
)
811 for( i
= 0 ; i
< p_sd
->p_sys
->i_announces
; i
++ )
813 sap_announce_t
* p_announce
= p_sd
->p_sys
->pp_announces
[i
];
815 /* FIXME: we create a new announce each time the sdp changes */
816 if( IsSameSession( p_announce
->p_sdp
, p_sdp
) )
818 /* We don't support delete announcement as they can easily
819 * Be used to highjack an announcement by a third party.
820 * Intead we cleverly implement Implicit Announcement removal.
822 * if( b_need_delete )
823 * RemoveAnnounce( p_sd, p_sd->p_sys->pp_announces[i]);
829 /* No need to go after six, as we start to trust the
830 * average period at six */
831 if( p_announce
->i_period_trust
<= 5 )
832 p_announce
->i_period_trust
++;
834 /* Compute the average period */
835 mtime_t now
= mdate();
836 p_announce
->i_period
= (p_announce
->i_period
+ (now
- p_announce
->i_last
)) / 2;
837 p_announce
->i_last
= now
;
839 FreeSDP( p_sdp
); p_sdp
= NULL
;
844 CreateAnnounce( p_sd
, i_hash
, p_sdp
);
850 sap_announce_t
*CreateAnnounce( services_discovery_t
*p_sd
, uint16_t i_hash
,
853 input_item_t
*p_input
;
854 const char *psz_value
;
855 sap_announce_t
*p_sap
= (sap_announce_t
*)malloc(
856 sizeof(sap_announce_t
) );
857 services_discovery_sys_t
*p_sys
;
863 p_sap
->i_last
= mdate();
865 p_sap
->i_period_trust
= 0;
866 p_sap
->i_hash
= i_hash
;
867 p_sap
->p_sdp
= p_sdp
;
869 /* Released in RemoveAnnounce */
870 p_input
= input_item_NewWithType( VLC_OBJECT(p_sd
),
871 p_sap
->p_sdp
->psz_uri
,
872 p_sdp
->psz_sessionname
,
873 0, NULL
, 0, -1, ITEM_TYPE_NET
);
874 p_sap
->p_item
= p_input
;
881 if( p_sdp
->rtcp_port
)
884 if( asprintf( &rtcp
, ":rtcp-port=%u", p_sdp
->rtcp_port
) != -1 )
886 input_item_AddOption( p_input
, rtcp
, VLC_INPUT_OPTION_TRUSTED
);
891 psz_value
= GetAttribute( p_sap
->p_sdp
->pp_attributes
, p_sap
->p_sdp
->i_attributes
, "tool" );
892 if( psz_value
!= NULL
)
894 input_item_AddInfo( p_input
, _("Session"), _("Tool"), "%s", psz_value
);
896 if( strcmp( p_sdp
->username
, "-" ) )
898 input_item_AddInfo( p_input
, _("Session"), _("User"), "%s",
903 if (p_sap
->p_sdp
->mediac
>= 1)
904 psz_value
= FindAttribute (p_sap
->p_sdp
, 0, "x-plgroup");
906 psz_value
= GetAttribute( p_sap
->p_sdp
->pp_attributes
, p_sap
->p_sdp
->i_attributes
, "x-plgroup" );
908 services_discovery_AddItem( p_sd
, p_input
, psz_value
/* category name */ );
910 TAB_APPEND( p_sys
->i_announces
, p_sys
->pp_announces
, p_sap
);
916 static const char *FindAttribute (const sdp_t
*sdp
, unsigned media
,
919 /* Look for media attribute, and fallback to session */
920 const char *attr
= GetAttribute (sdp
->mediav
[media
].pp_attributes
,
921 sdp
->mediav
[media
].i_attributes
, name
);
923 attr
= GetAttribute (sdp
->pp_attributes
, sdp
->i_attributes
, name
);
928 /* Fill p_sdp->psz_uri */
929 static int ParseConnection( vlc_object_t
*p_obj
, sdp_t
*p_sdp
)
931 if (p_sdp
->mediac
== 0)
933 msg_Dbg (p_obj
, "Ignoring SDP with no media");
937 for (unsigned i
= 1; i
< p_sdp
->mediac
; i
++)
939 if ((p_sdp
->mediav
[i
].n_addr
!= p_sdp
->mediav
->n_addr
)
940 || (p_sdp
->mediav
[i
].addrlen
!= p_sdp
->mediav
->addrlen
)
941 || memcmp (&p_sdp
->mediav
[i
].addr
, &p_sdp
->mediav
->addr
,
942 p_sdp
->mediav
->addrlen
))
944 msg_Dbg (p_obj
, "Multiple media ports not supported -> live555");
949 if (p_sdp
->mediav
->n_addr
!= 1)
951 msg_Dbg (p_obj
, "Layered encoding not supported -> live555");
960 if (vlc_getnameinfo ((struct sockaddr
*)&(p_sdp
->mediav
->addr
),
961 p_sdp
->mediav
->addrlen
, psz_uri
+ 1,
962 sizeof (psz_uri
) - 2, &port
, NI_NUMERICHOST
))
965 if (strchr (psz_uri
+ 1, ':'))
968 strcat (psz_uri
, "]");
974 char *sdp_proto
= strdup (p_sdp
->mediav
[0].fmt
);
975 if (sdp_proto
== NULL
)
978 char *subtype
= strchr (sdp_proto
, ' ');
981 msg_Dbg (p_obj
, "missing SDP media subtype: %s", sdp_proto
);
988 /* FIXME: check for multiple payload types in RTP/AVP case.
989 * FIXME: check for "mpeg" subtype in raw udp case. */
990 if (!strcasecmp (sdp_proto
, "udp"))
991 p_sdp
->i_media_type
= 33;
993 p_sdp
->i_media_type
= atoi (subtype
);
996 /* RTP protocol, nul, VLC shortcut, nul, flags byte as follow:
997 * 0x1: Connection-Oriented media. */
998 static const char proto_match
[] =
1000 "RTP/AVP\0" "rtp\0\0"
1001 "UDPLite/RTP/AVP\0" "udplite\0\0"
1002 "DCCP/RTP/AVP\0" "dccp\0\1"
1003 "TCP/RTP/AVP\0" "rtptcp\0\1"
1006 const char *vlc_proto
= NULL
;
1008 for (const char *proto
= proto_match
; *proto
;)
1010 if (strcasecmp (proto
, sdp_proto
) == 0)
1012 vlc_proto
= proto
+ strlen (proto
) + 1;
1013 flags
= vlc_proto
[strlen (vlc_proto
) + 1];
1016 proto
+= strlen (proto
) + 1;
1017 proto
+= strlen (proto
) + 2;
1021 if (vlc_proto
== NULL
)
1023 msg_Dbg (p_obj
, "unknown SDP media protocol: %s",
1024 p_sdp
->mediav
[0].fmt
);
1025 return VLC_EGENERIC
;
1028 if (!strcmp (vlc_proto
, "udp") || FindAttribute (p_sdp
, 0, "rtcp-mux"))
1029 p_sdp
->rtcp_port
= 0;
1032 const char *rtcp
= FindAttribute (p_sdp
, 0, "rtcp");
1034 p_sdp
->rtcp_port
= atoi (rtcp
);
1036 if (port
& 1) /* odd port -> RTCP; next even port -> RTP */
1037 p_sdp
->rtcp_port
= port
++;
1038 else /* even port -> RTP; next odd port -> RTCP */
1039 p_sdp
->rtcp_port
= port
+ 1;
1044 /* Connection-oriented media */
1045 const char *setup
= FindAttribute (p_sdp
, 0, "setup");
1047 setup
= "active"; /* default value */
1049 if (strcmp (setup
, "actpass") && strcmp (setup
, "passive"))
1051 msg_Dbg (p_obj
, "unsupported COMEDIA mode: %s", setup
);
1052 return VLC_EGENERIC
;
1055 if (asprintf (&p_sdp
->psz_uri
, "%s://%s:%d", vlc_proto
,
1061 /* Non-connected (normally multicast) media */
1062 char psz_source
[258] = "";
1063 const char *sfilter
= FindAttribute (p_sdp
, 0, "source-filter");
1064 if (sfilter
!= NULL
)
1066 char psz_source_ip
[256];
1069 if (sscanf (sfilter
, " incl IN IP%u %*s %255s ", &ipv
,
1070 psz_source_ip
) == 2)
1072 /* According to RFC4570, FQDNs can be used for source-filters,
1073 * but -seriously- this is impractical */
1079 struct in6_addr addr
;
1080 if ((inet_pton (AF_INET6
, psz_source_ip
, &addr
) > 0)
1081 && (inet_ntop (AF_INET6
, &addr
, psz_source
+ 1,
1082 sizeof (psz_source
) - 2) != NULL
))
1084 psz_source
[0] = '[';
1085 psz_source
[strlen (psz_source
)] = ']';
1092 struct in_addr addr
;
1093 if ((inet_pton (AF_INET
, psz_source_ip
, &addr
) > 0)
1094 && (inet_ntop (AF_INET
, &addr
, psz_source
,
1095 sizeof (psz_source
)) == NULL
))
1103 if (asprintf (&p_sdp
->psz_uri
, "%s://%s@%s:%i", vlc_proto
, psz_source
,
1112 static int ParseSDPConnection (const char *str
, struct sockaddr_storage
*addr
,
1113 socklen_t
*addrlen
, unsigned *number
)
1116 unsigned fam
, n1
, n2
;
1118 int res
= sscanf (str
, "IN IP%u %59[^/]/%u/%u", &fam
, host
, &n1
, &n2
);
1126 addr
->ss_family
= AF_INET6
;
1130 *addrlen
= sizeof (struct sockaddr_in6
);
1132 if (inet_pton (AF_INET6
, host
,
1133 &((struct sockaddr_in6
*)addr
)->sin6_addr
) <= 0)
1136 *number
= (res
>= 3) ? n1
: 1;
1141 addr
->ss_family
= AF_INET
;
1145 *addrlen
= sizeof (struct sockaddr_in
);
1147 if (inet_pton (AF_INET
, host
,
1148 &((struct sockaddr_in
*)addr
)->sin_addr
) <= 0)
1151 *number
= (res
>= 4) ? n2
: 1;
1161 /***********************************************************************
1162 * ParseSDP : SDP parsing
1163 * *********************************************************************
1164 * Validate SDP and parse all fields
1165 ***********************************************************************/
1166 static sdp_t
*ParseSDP (vlc_object_t
*p_obj
, const char *psz_sdp
)
1168 if( psz_sdp
== NULL
)
1171 sdp_t
*p_sdp
= calloc (1, sizeof (*p_sdp
));
1176 struct sockaddr_storage glob_addr
;
1177 memset (&glob_addr
, 0, sizeof (glob_addr
));
1178 socklen_t glob_len
= 0;
1179 unsigned glob_count
= 1;
1182 /* TODO: use iconv and charset attribute instead of EnsureUTF8 */
1185 /* Extract one line */
1186 char *eol
= strchr (psz_sdp
, '\n');
1187 size_t linelen
= eol
? (size_t)(eol
- psz_sdp
) : strlen (psz_sdp
);
1188 char line
[linelen
+ 1];
1189 memcpy (line
, psz_sdp
, linelen
);
1190 line
[linelen
] = '\0';
1192 psz_sdp
+= linelen
+ 1;
1194 /* Remove carriage return if present */
1195 eol
= strchr (line
, '\r');
1198 linelen
= eol
- line
;
1199 line
[linelen
] = '\0';
1203 char cat
= line
[0], *data
= line
+ 2;
1204 if (!cat
|| (strchr ("vosiuepcbtrzkam", cat
) == NULL
))
1206 /* MUST ignore SDP with unknown line type */
1207 msg_Dbg (p_obj
, "unknown SDP line type: 0x%02x", (int)cat
);
1212 msg_Dbg (p_obj
, "invalid SDP line: %s", line
);
1216 assert (linelen
>= 2);
1218 /* SDP parsing state machine
1219 * We INTERNALLY use uppercase for session, lowercase for media
1223 /* Session description */
1228 msg_Dbg (p_obj
, "missing SDP version");
1231 if (strcmp (data
, "0"))
1233 msg_Dbg (p_obj
, "unknown SDP version: %s", data
);
1243 msg_Dbg (p_obj
, "missing SDP originator");
1247 if ((sscanf (data
, "%63s %"SCNu64
" %"SCNu64
" IN IP%u %1023s",
1248 p_sdp
->username
, &p_sdp
->session_id
,
1249 &p_sdp
->session_version
, &p_sdp
->orig_ip_version
,
1250 p_sdp
->orig_host
) != 5)
1251 || ((p_sdp
->orig_ip_version
!= 4)
1252 && (p_sdp
->orig_ip_version
!= 6)))
1254 msg_Dbg (p_obj
, "SDP origin not supported: %s", data
);
1255 /* Or maybe out-of-range, but this looks suspicious */
1258 EnsureUTF8 (p_sdp
->orig_host
);
1265 if ((cat
!= 's') || !*data
)
1267 /* MUST be present AND non-empty */
1268 msg_Dbg (p_obj
, "missing SDP session name");
1271 assert (p_sdp
->psz_sessionname
== NULL
); // no memleak here
1272 p_sdp
->psz_sessionname
= strdup (data
);
1273 if (p_sdp
->psz_sessionname
== NULL
)
1275 EnsureUTF8 (p_sdp
->psz_sessionname
);
1299 if (ParseSDPConnection (data
, &glob_addr
, &glob_len
,
1302 msg_Dbg (p_obj
, "SDP connection infos not supported: "
1309 assert (expect
== 'B');
1316 msg_Dbg (p_obj
, "missing SDP time description");
1322 if ((cat
== 't') || (cat
== 'r'))
1337 attribute_t
*p_attr
= MakeAttribute (data
);
1338 TAB_APPEND( p_sdp
->i_attributes
, p_sdp
->pp_attributes
, p_attr
);
1342 /* Media description */
1349 msg_Dbg (p_obj
, "missing SDP media description");
1352 struct sdp_media_t
*m
;
1353 m
= realloc (p_sdp
->mediav
, (p_sdp
->mediac
+ 1) * sizeof (*m
));
1361 memset (m
, 0, sizeof (*m
));
1362 memcpy (&m
->addr
, &glob_addr
, m
->addrlen
= glob_len
);
1363 m
->n_addr
= glob_count
;
1365 /* TODO: remember media type (if we need multiple medias) */
1366 data
= strchr (data
, ' ');
1369 msg_Dbg (p_obj
, "missing SDP media port");
1372 port
= atoi (++data
);
1373 if (port
<= 0 || port
>= 65536)
1375 msg_Dbg (p_obj
, "invalid transport port %d", port
);
1378 net_SetPort ((struct sockaddr
*)&m
->addr
, htons (port
));
1380 data
= strchr (data
, ' ');
1383 msg_Dbg (p_obj
, "missing SDP media format");
1386 m
->fmt
= strdup (++data
);
1400 struct sdp_media_t
*m
= p_sdp
->mediav
+ p_sdp
->mediac
- 1;
1401 if (ParseSDPConnection (data
, &m
->addr
, &m
->addrlen
,
1404 msg_Dbg (p_obj
, "SDP connection infos not supported: "
1408 net_SetPort ((struct sockaddr
*)&m
->addr
, htons (port
));
1420 assert (expect
== 'a');
1423 attribute_t
*p_attr
= MakeAttribute (data
);
1427 TAB_APPEND (p_sdp
->mediav
[p_sdp
->mediac
- 1].i_attributes
,
1428 p_sdp
->mediav
[p_sdp
->mediac
- 1].pp_attributes
, p_attr
);
1437 msg_Dbg (p_obj
, "unexpected SDP line: 0x%02x", (int)cat
);
1443 msg_Err (p_obj
, "*** BUG in SDP parser! ***");
1455 static int InitSocket( services_discovery_t
*p_sd
, const char *psz_address
,
1458 int i_fd
= net_ListenUDP1 ((vlc_object_t
*)p_sd
, psz_address
, i_port
);
1460 return VLC_EGENERIC
;
1462 shutdown( i_fd
, SHUT_WR
);
1463 INSERT_ELEM (p_sd
->p_sys
->pi_fd
, p_sd
->p_sys
->i_fd
,
1464 p_sd
->p_sys
->i_fd
, i_fd
);
1468 static int Decompress( const unsigned char *psz_src
, unsigned char **_dst
, int i_len
)
1471 int i_result
, i_dstsize
, n
= 0;
1472 unsigned char *psz_dst
= NULL
;
1475 memset (&d_stream
, 0, sizeof (d_stream
));
1477 i_result
= inflateInit(&d_stream
);
1478 if( i_result
!= Z_OK
)
1481 d_stream
.next_in
= (Bytef
*)psz_src
;
1482 d_stream
.avail_in
= i_len
;
1487 psz_dst
= (unsigned char *)realloc( psz_dst
, n
* 1000 );
1488 d_stream
.next_out
= (Bytef
*)&psz_dst
[(n
- 1) * 1000];
1489 d_stream
.avail_out
= 1000;
1491 i_result
= inflate(&d_stream
, Z_NO_FLUSH
);
1492 if( ( i_result
!= Z_OK
) && ( i_result
!= Z_STREAM_END
) )
1494 inflateEnd( &d_stream
);
1499 while( ( d_stream
.avail_out
== 0 ) && ( d_stream
.avail_in
!= 0 ) &&
1500 ( i_result
!= Z_STREAM_END
) );
1502 i_dstsize
= d_stream
.total_out
;
1503 inflateEnd( &d_stream
);
1505 *_dst
= (unsigned char *)realloc( psz_dst
, i_dstsize
);
1517 static void FreeSDP( sdp_t
*p_sdp
)
1519 free( p_sdp
->psz_sessionname
);
1520 free( p_sdp
->psz_uri
);
1522 for (unsigned j
= 0; j
< p_sdp
->mediac
; j
++)
1524 free (p_sdp
->mediav
[j
].fmt
);
1525 for (int i
= 0; i
< p_sdp
->mediav
[j
].i_attributes
; i
++)
1526 FreeAttribute (p_sdp
->mediav
[j
].pp_attributes
[i
]);
1527 free (p_sdp
->mediav
[j
].pp_attributes
);
1529 free (p_sdp
->mediav
);
1531 for (int i
= 0; i
< p_sdp
->i_attributes
; i
++)
1532 FreeAttribute (p_sdp
->pp_attributes
[i
]);
1534 free (p_sdp
->pp_attributes
);
1538 static int RemoveAnnounce( services_discovery_t
*p_sd
,
1539 sap_announce_t
*p_announce
)
1543 if( p_announce
->p_sdp
)
1545 FreeSDP( p_announce
->p_sdp
);
1546 p_announce
->p_sdp
= NULL
;
1549 if( p_announce
->p_item
)
1551 services_discovery_RemoveItem( p_sd
, p_announce
->p_item
);
1552 vlc_gc_decref( p_announce
->p_item
);
1553 p_announce
->p_item
= NULL
;
1556 for( i
= 0; i
< p_sd
->p_sys
->i_announces
; i
++)
1558 if( p_sd
->p_sys
->pp_announces
[i
] == p_announce
)
1560 REMOVE_ELEM( p_sd
->p_sys
->pp_announces
, p_sd
->p_sys
->i_announces
,
1571 static bool IsSameSession( sdp_t
*p_sdp1
, sdp_t
*p_sdp2
)
1573 /* A session is identified by
1576 * - network type (which is always IN),
1577 * - address type (currently, this means IP version),
1580 if (strcmp (p_sdp1
->username
, p_sdp2
->username
)
1581 || (p_sdp1
->session_id
!= p_sdp2
->session_id
)
1582 || (p_sdp1
->orig_ip_version
!= p_sdp2
->orig_ip_version
)
1583 || strcmp (p_sdp1
->orig_host
, p_sdp2
->orig_host
))
1590 static inline attribute_t
*MakeAttribute (const char *str
)
1592 attribute_t
*a
= malloc (sizeof (*a
) + strlen (str
) + 1);
1596 strcpy (a
->name
, str
);
1597 EnsureUTF8 (a
->name
);
1598 char *value
= strchr (a
->name
, ':');
1610 static const char *GetAttribute (attribute_t
**tab
, unsigned n
,
1613 for (unsigned i
= 0; i
< n
; i
++)
1614 if (strcasecmp (tab
[i
]->name
, name
) == 0)
1615 return tab
[i
]->value
;
1620 static inline void FreeAttribute (attribute_t
*a
)