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>
45 #ifdef HAVE_ARPA_INET_H
46 # include <arpa/inet.h>
60 /************************************************************************
61 * Macros and definitions
62 ************************************************************************/
64 #define MAX_LINE_LENGTH 256
66 /* SAP is always on that port */
68 /* Global-scope SAP address */
69 #define SAP_V4_GLOBAL_ADDRESS "224.2.127.254"
70 /* Organization-local SAP address */
71 #define SAP_V4_ORG_ADDRESS "239.195.255.255"
72 /* Local (smallest non-link-local scope) SAP address */
73 #define SAP_V4_LOCAL_ADDRESS "239.255.255.255"
74 /* Link-local SAP address */
75 #define SAP_V4_LINK_ADDRESS "224.0.0.255"
78 /*****************************************************************************
80 *****************************************************************************/
81 #define SAP_ADDR_TEXT N_( "SAP multicast address" )
82 #define SAP_ADDR_LONGTEXT N_( "The SAP module normally chooses itself the " \
83 "right addresses to listen to. However, you " \
84 "can specify a specific address." )
85 #define SAP_TIMEOUT_TEXT N_( "SAP timeout (seconds)" )
86 #define SAP_TIMEOUT_LONGTEXT N_( \
87 "Delay after which SAP items get deleted if no new announcement " \
89 #define SAP_PARSE_TEXT N_( "Try to parse the announce" )
90 #define SAP_PARSE_LONGTEXT N_( \
91 "This enables actual parsing of the announces by the SAP module. " \
92 "Otherwise, all announcements are parsed by the \"live555\" " \
93 "(RTP/RTSP) module." )
94 #define SAP_STRICT_TEXT N_( "SAP Strict mode" )
95 #define SAP_STRICT_LONGTEXT N_( \
96 "When this is set, the SAP parser will discard some non-compliant " \
100 static int Open ( vlc_object_t
* );
101 static void Close( vlc_object_t
* );
102 static int OpenDemux ( vlc_object_t
* );
103 static void CloseDemux ( vlc_object_t
* );
105 VLC_SD_PROBE_HELPER("sap", "Network streams (SAP)", SD_CAT_LAN
)
108 set_shortname( N_("SAP"))
109 set_description( N_("Network streams (SAP)") )
110 set_category( CAT_PLAYLIST
)
111 set_subcategory( SUBCAT_PLAYLIST_SD
)
113 add_string( "sap-addr", NULL
,
114 SAP_ADDR_TEXT
, SAP_ADDR_LONGTEXT
, true )
115 add_obsolete_bool( "sap-ipv4" ) /* since 2.0.0 */
116 add_obsolete_bool( "sap-ipv6" ) /* since 2.0.0 */
117 add_integer( "sap-timeout", 1800,
118 SAP_TIMEOUT_TEXT
, SAP_TIMEOUT_LONGTEXT
, true )
119 add_bool( "sap-parse", true,
120 SAP_PARSE_TEXT
,SAP_PARSE_LONGTEXT
, true )
121 add_bool( "sap-strict", false,
122 SAP_STRICT_TEXT
,SAP_STRICT_LONGTEXT
, true )
123 add_obsolete_bool( "sap-timeshift" ) /* Redumdant since 1.0.0 */
125 set_capability( "services_discovery", 0 )
126 set_callbacks( Open
, Close
)
128 VLC_SD_PROBE_SUBMODULE
131 set_description( N_("SDP Descriptions parser") )
132 add_shortcut( "sdp" )
133 set_capability( "demux", 51 )
134 set_callbacks( OpenDemux
, CloseDemux
)
138 /*****************************************************************************
140 *****************************************************************************/
142 typedef struct sdp_t sdp_t
;
143 typedef struct attribute_t attribute_t
;
144 typedef struct sap_announce_t sap_announce_t
;
149 struct sdp_t
*parent
;
151 struct sockaddr_storage addr
;
155 attribute_t
**pp_attributes
;
159 /* The structure that contains sdp information */
167 uint64_t session_version
;
168 unsigned orig_ip_version
;
169 char orig_host
[1024];
172 char *psz_sessionname
;
175 char *psz_sessioninfo
;
183 /* a= global attributes */
185 attribute_t
**pp_attributes
;
187 /* medias (well, we only support one atm) */
189 struct sdp_media_t
*mediav
;
198 struct sap_announce_t
202 uint8_t i_period_trust
;
205 uint32_t i_source
[4];
207 /* SAP annnounces must only contain one SDP */
210 input_item_t
* p_item
;
213 struct services_discovery_sys_t
217 /* Socket descriptors */
221 /* Table of announces */
223 struct sap_announce_t
**pp_announces
;
237 /*****************************************************************************
239 *****************************************************************************/
243 static int Demux( demux_t
*p_demux
);
244 static int Control( demux_t
*, int, va_list );
245 static void *Run ( void *p_sd
);
247 /* Main parsing functions */
248 static int ParseConnection( vlc_object_t
*p_obj
, sdp_t
*p_sdp
);
249 static int ParseSAP( services_discovery_t
*p_sd
, const uint8_t *p_buffer
, size_t i_read
);
250 static sdp_t
*ParseSDP (vlc_object_t
*p_sd
, const char *psz_sdp
);
251 static sap_announce_t
*CreateAnnounce( services_discovery_t
*, uint32_t *, uint16_t, sdp_t
* );
252 static int RemoveAnnounce( services_discovery_t
*p_sd
, sap_announce_t
*p_announce
);
254 /* Helper functions */
255 static inline attribute_t
*MakeAttribute (const char *str
);
256 static const char *GetAttribute (attribute_t
**tab
, unsigned n
, const char *name
);
257 static inline void FreeAttribute (attribute_t
*a
);
258 static const char *FindAttribute (const sdp_t
*sdp
, unsigned media
,
261 static bool IsSameSession( sdp_t
*p_sdp1
, sdp_t
*p_sdp2
);
262 static int InitSocket( services_discovery_t
*p_sd
, const char *psz_address
, int i_port
);
263 static int Decompress( const unsigned char *psz_src
, unsigned char **_dst
, int i_len
);
264 static void FreeSDP( sdp_t
*p_sdp
);
266 static inline int min_int( int a
, int b
)
268 return a
> b
? b
: a
;
271 static bool IsWellKnownPayload (int type
)
274 { /* Should be in sync with modules/demux/rtp.c */
275 case 0: /* PCMU/8000 */
277 case 8: /* PCMA/8000 */
278 case 10: /* L16/44100/2 */
279 case 11: /* L16/44100 */
281 case 14: /* MPA/90000 */
282 case 32: /* MPV/90000 */
283 case 33: /* MP2/90000 */
289 /*****************************************************************************
290 * Open: initialize and create stuff
291 *****************************************************************************/
292 static int Open( vlc_object_t
*p_this
)
294 services_discovery_t
*p_sd
= ( services_discovery_t
* )p_this
;
295 services_discovery_sys_t
*p_sys
= (services_discovery_sys_t
*)
296 malloc( sizeof( services_discovery_sys_t
) );
300 p_sys
->i_timeout
= var_CreateGetInteger( p_sd
, "sap-timeout" );
307 p_sys
->b_strict
= var_CreateGetBool( p_sd
, "sap-strict");
308 p_sys
->b_parse
= var_CreateGetBool( p_sd
, "sap-parse" );
310 p_sys
->i_announces
= 0;
311 p_sys
->pp_announces
= NULL
;
312 /* TODO: create sockets here, and fix racy sockets table */
313 if (vlc_clone (&p_sys
->thread
, Run
, p_sd
, VLC_THREAD_PRIORITY_LOW
))
322 /*****************************************************************************
323 * OpenDemux: initialize and create stuff
324 *****************************************************************************/
325 static int OpenDemux( vlc_object_t
*p_this
)
327 demux_t
*p_demux
= (demux_t
*)p_this
;
328 const uint8_t *p_peek
;
329 char *psz_sdp
= NULL
;
331 int errval
= VLC_EGENERIC
;
334 if( !var_CreateGetBool( p_demux
, "sap-parse" ) )
336 /* We want livedotcom module to parse this SDP file */
340 assert( p_demux
->s
); /* this is NOT an access_demux */
343 if( stream_Peek( p_demux
->s
, &p_peek
, 7 ) < 7 )
346 if( memcmp( p_peek
, "v=0\r\no=", 7 ) && memcmp( p_peek
, "v=0\no=", 6 ) )
349 /* Gather the complete sdp file */
350 for( i_len
= 0, psz_sdp
= NULL
; i_len
< 65536; )
352 const int i_read_max
= 1024;
353 char *psz_sdp_new
= realloc( psz_sdp
, i_len
+ i_read_max
+ 1 );
355 if( psz_sdp_new
== NULL
)
360 psz_sdp
= psz_sdp_new
;
362 i_read
= stream_Read( p_demux
->s
, &psz_sdp
[i_len
], i_read_max
);
363 if( (int)i_read
< 0 )
365 msg_Err( p_demux
, "cannot read SDP" );
370 psz_sdp
[i_len
] = '\0';
372 if( (int)i_read
< i_read_max
)
376 p_sdp
= ParseSDP( VLC_OBJECT(p_demux
), psz_sdp
);
380 msg_Warn( p_demux
, "invalid SDP");
384 if( ParseConnection( VLC_OBJECT( p_demux
), p_sdp
) )
386 p_sdp
->psz_uri
= NULL
;
388 if (!IsWellKnownPayload (p_sdp
->i_media_type
))
390 if( p_sdp
->psz_uri
== NULL
) goto error
;
392 p_demux
->p_sys
= (demux_sys_t
*)malloc( sizeof(demux_sys_t
) );
393 if( unlikely( !p_demux
->p_sys
) )
395 p_demux
->p_sys
->p_sdp
= p_sdp
;
396 p_demux
->pf_control
= Control
;
397 p_demux
->pf_demux
= Demux
;
404 if( p_sdp
) FreeSDP( p_sdp
); p_sdp
= NULL
;
405 stream_Seek( p_demux
->s
, 0 );
409 /*****************************************************************************
411 *****************************************************************************/
412 static void Close( vlc_object_t
*p_this
)
414 services_discovery_t
*p_sd
= ( services_discovery_t
* )p_this
;
415 services_discovery_sys_t
*p_sys
= p_sd
->p_sys
;
418 vlc_cancel (p_sys
->thread
);
419 vlc_join (p_sys
->thread
, NULL
);
421 for( i
= p_sys
->i_fd
-1 ; i
>= 0 ; i
-- )
423 net_Close( p_sys
->pi_fd
[i
] );
425 FREENULL( p_sys
->pi_fd
);
427 for( i
= p_sys
->i_announces
- 1; i
>= 0; i
-- )
429 RemoveAnnounce( p_sd
, p_sys
->pp_announces
[i
] );
431 FREENULL( p_sys
->pp_announces
);
436 /*****************************************************************************
437 * CloseDemux: Close the demuxer
438 *****************************************************************************/
439 static void CloseDemux( vlc_object_t
*p_this
)
441 demux_t
*p_demux
= (demux_t
*)p_this
;
443 if( p_demux
->p_sys
->p_sdp
)
444 FreeSDP( p_demux
->p_sys
->p_sdp
);
445 free( p_demux
->p_sys
);
448 /*****************************************************************************
449 * Run: main SAP thread
450 *****************************************************************************
451 * Listens to SAP packets, and sends them to packet_handle
452 *****************************************************************************/
453 #define MAX_SAP_BUFFER 5000
455 static void *Run( void *data
)
457 services_discovery_t
*p_sd
= data
;
461 int canc
= vlc_savecancel ();
463 /* Braindead Winsock DNS resolver will get stuck over 2 seconds per failed
464 * DNS queries, even if the DNS server returns an error with milliseconds.
465 * You don't want to know why the bug (as of XP SP2) wasn't fixed since
466 * Winsock 1.1 from Windows 95, if not Windows 3.1.
467 * Anyway, to avoid a 30 seconds delay for failed IPv6 socket creation,
468 * we have to open sockets in Run() rather than Open(). */
469 InitSocket( p_sd
, SAP_V4_GLOBAL_ADDRESS
, SAP_PORT
);
470 InitSocket( p_sd
, SAP_V4_ORG_ADDRESS
, SAP_PORT
);
471 InitSocket( p_sd
, SAP_V4_LOCAL_ADDRESS
, SAP_PORT
);
472 InitSocket( p_sd
, SAP_V4_LINK_ADDRESS
, SAP_PORT
);
474 char psz_address
[NI_MAXNUMERICHOST
] = "ff02::2:7ffe%";
476 struct if_nameindex
*l
= if_nameindex ();
479 char *ptr
= strchr (psz_address
, '%') + 1;
480 for (unsigned i
= 0; l
[i
].if_index
; i
++)
482 strcpy (ptr
, l
[i
].if_name
);
483 InitSocket (p_sd
, psz_address
, SAP_PORT
);
485 if_freenameindex (l
);
488 /* this is the Winsock2 equivalant of SIOCGIFCONF on BSD stacks,
489 which if_nameindex uses internally anyway */
491 // first create a dummy socket to pin down the protocol family
492 SOCKET s
= socket(PF_INET6
, SOCK_DGRAM
, IPPROTO_UDP
);
493 if( s
!= INVALID_SOCKET
)
495 INTERFACE_INFO ifaces
[10]; // Assume there will be no more than 10 IP interfaces
496 DWORD len
= sizeof(ifaces
);
498 if( SOCKET_ERROR
!= WSAIoctl(s
, SIO_GET_INTERFACE_LIST
, NULL
, 0, &ifaces
, len
, &len
, NULL
, NULL
) )
500 unsigned ifcount
= len
/sizeof(INTERFACE_INFO
);
501 char *ptr
= strchr (psz_address
, '%') + 1;
502 for(unsigned i
= 1; i
<=ifcount
; ++i
)
504 // append link-local zone identifier
505 sprintf(ptr
, "%d", i
);
511 *strchr (psz_address
, '%') = '\0';
513 static const char ipv6_scopes
[] = "1456789ABCDE";
514 for (const char *c_scope
= ipv6_scopes
; *c_scope
; c_scope
++)
516 psz_address
[3] = *c_scope
;
517 InitSocket( p_sd
, psz_address
, SAP_PORT
);
520 psz_addr
= var_CreateGetString( p_sd
, "sap-addr" );
521 if( psz_addr
&& *psz_addr
)
522 InitSocket( p_sd
, psz_addr
, SAP_PORT
);
525 if( p_sd
->p_sys
->i_fd
== 0 )
527 msg_Err( p_sd
, "unable to listen on any address" );
531 /* read SAP packets */
534 vlc_restorecancel (canc
);
535 unsigned n
= p_sd
->p_sys
->i_fd
;
536 struct pollfd ufd
[n
];
538 for (unsigned i
= 0; i
< n
; i
++)
540 ufd
[i
].fd
= p_sd
->p_sys
->pi_fd
[i
];
541 ufd
[i
].events
= POLLIN
;
545 int val
= poll (ufd
, n
, timeout
);
546 canc
= vlc_savecancel ();
549 for (unsigned i
= 0; i
< n
; i
++)
553 uint8_t p_buffer
[MAX_SAP_BUFFER
+1];
556 i_read
= net_Read (p_sd
, ufd
[i
].fd
, NULL
, p_buffer
,
557 MAX_SAP_BUFFER
, false);
559 msg_Warn (p_sd
, "receive error: %s",
560 vlc_strerror_c(errno
));
563 /* Parse the packet */
564 p_buffer
[i_read
] = '\0';
565 ParseSAP (p_sd
, p_buffer
, i_read
);
571 mtime_t now
= mdate();
573 /* A 1 hour timeout correspond to the RFC Implicit timeout.
574 * This timeout is tuned in the following loop. */
575 timeout
= 1000 * 60 * 60;
577 /* Check for items that need deletion */
578 for( i
= 0; i
< p_sd
->p_sys
->i_announces
; i
++ )
580 mtime_t i_timeout
= ( mtime_t
) 1000000 * p_sd
->p_sys
->i_timeout
;
581 sap_announce_t
* p_announce
= p_sd
->p_sys
->pp_announces
[i
];
582 mtime_t i_last_period
= now
- p_announce
->i_last
;
584 /* Remove the announcement, if the last announcement was 1 hour ago
585 * or if the last packet emitted was 3 times the average time
586 * between two packets */
587 if( ( p_announce
->i_period_trust
> 5 && i_last_period
> 3 * p_announce
->i_period
) ||
588 i_last_period
> i_timeout
)
590 RemoveAnnounce( p_sd
, p_announce
);
594 /* Compute next timeout */
595 if( p_announce
->i_period_trust
> 5 )
596 timeout
= min_int((3 * p_announce
->i_period
- i_last_period
) / 1000, timeout
);
597 timeout
= min_int((i_timeout
- i_last_period
)/1000, timeout
);
601 if( !p_sd
->p_sys
->i_announces
)
602 timeout
= -1; /* We can safely poll indefinitely. */
603 else if( timeout
< 200 )
604 timeout
= 200; /* Don't wakeup too fast. */
609 /**********************************************************************
610 * Demux: reads and demuxes data packets
611 * Return -1 if error, 0 if EOF, 1 else
612 **********************************************************************/
613 static int Demux( demux_t
*p_demux
)
615 sdp_t
*p_sdp
= p_demux
->p_sys
->p_sdp
;
616 input_thread_t
*p_input
;
617 input_item_t
*p_parent_input
;
619 p_input
= demux_GetParentInput( p_demux
);
623 msg_Err( p_demux
, "parent input could not be found" );
627 /* This item hasn't been held by input_GetItem
628 * don't release it */
629 p_parent_input
= input_GetItem( p_input
);
631 input_item_SetURI( p_parent_input
, p_sdp
->psz_uri
);
632 input_item_SetName( p_parent_input
, p_sdp
->psz_sessionname
);
633 if( p_sdp
->rtcp_port
)
636 if( asprintf( &rtcp
, ":rtcp-port=%u", p_sdp
->rtcp_port
) != -1 )
638 input_item_AddOption( p_parent_input
, rtcp
, VLC_INPUT_OPTION_TRUSTED
);
643 vlc_mutex_lock( &p_parent_input
->lock
);
645 p_parent_input
->i_type
= ITEM_TYPE_NET
;
647 vlc_mutex_unlock( &p_parent_input
->lock
);
648 vlc_object_release( p_input
);
652 static int Control( demux_t
*p_demux
, int i_query
, va_list args
)
654 VLC_UNUSED(p_demux
); VLC_UNUSED(i_query
); VLC_UNUSED(args
);
658 /**************************************************************
660 **************************************************************/
662 /* i_read is at least > 6 */
663 static int ParseSAP( services_discovery_t
*p_sd
, const uint8_t *buf
,
668 const uint8_t *end
= buf
+ len
;
670 uint32_t i_source
[4];
672 assert (buf
[len
] == '\0');
677 uint8_t flags
= buf
[0];
678 uint8_t auth_len
= buf
[1];
680 /* First, check the sap announce is correct */
681 if ((flags
>> 5) != 1)
684 bool b_ipv6
= (flags
& 0x10) != 0;
685 bool b_need_delete
= (flags
& 0x04) != 0;
689 msg_Dbg( p_sd
, "encrypted packet, unsupported" );
693 bool b_compressed
= (flags
& 0x01) != 0;
695 uint16_t i_hash
= U16_AT (buf
+ 2);
697 if( p_sd
->p_sys
->b_strict
&& i_hash
== 0 )
699 msg_Dbg( p_sd
, "strict mode, discarding announce with null id hash");
706 for( int i
= 0; i
< 4; i
++,buf
+=4)
707 i_source
[i
] = U32_AT(buf
);
711 memset(i_source
, 0, sizeof(i_source
));
712 i_source
[3] = U32_AT(buf
);
720 uint8_t *decomp
= NULL
;
723 int newsize
= Decompress (buf
, &decomp
, end
- buf
);
726 msg_Dbg( p_sd
, "decompression of SAP packet failed" );
730 decomp
= realloc (decomp
, newsize
+ 1);
731 decomp
[newsize
] = '\0';
732 psz_sdp
= (const char *)decomp
;
737 psz_sdp
= (const char *)buf
;
741 /* len is a strlen here here. both buf and decomp are len+1 where the 1 should be a \0 */
742 assert( psz_sdp
[len
] == '\0');
744 /* Skip payload type */
745 /* SAPv1 has implicit "application/sdp" payload type: first line is v=0 */
746 if (strncmp (psz_sdp
, "v=0", 3))
748 size_t clen
= strlen (psz_sdp
) + 1;
750 if (strcmp (psz_sdp
, "application/sdp"))
752 msg_Dbg (p_sd
, "unsupported content type: %s", psz_sdp
);
756 // skips content type
765 p_sdp
= ParseSDP( VLC_OBJECT(p_sd
), psz_sdp
);
770 p_sdp
->psz_sdp
= psz_sdp
;
772 /* Decide whether we should add a playlist item for this SDP */
773 /* Parse connection information (c= & m= ) */
774 if( ParseConnection( VLC_OBJECT(p_sd
), p_sdp
) )
775 p_sdp
->psz_uri
= NULL
;
777 /* Multi-media or no-parse -> pass to LIVE.COM */
778 if( !IsWellKnownPayload( p_sdp
->i_media_type
) || !p_sd
->p_sys
->b_parse
)
780 free( p_sdp
->psz_uri
);
781 if (asprintf( &p_sdp
->psz_uri
, "sdp://%s", p_sdp
->psz_sdp
) == -1)
782 p_sdp
->psz_uri
= NULL
;
785 if( p_sdp
->psz_uri
== NULL
)
791 for( i
= 0 ; i
< p_sd
->p_sys
->i_announces
; i
++ )
793 sap_announce_t
* p_announce
= p_sd
->p_sys
->pp_announces
[i
];
795 if( ( !i_hash
&& IsSameSession( p_announce
->p_sdp
, p_sdp
) )
796 || ( i_hash
&& p_announce
->i_hash
== i_hash
797 && !memcmp(p_announce
->i_source
, i_source
, sizeof(i_source
)) ) )
799 /* We don't support delete announcement as they can easily
800 * Be used to highjack an announcement by a third party.
801 * Instead we cleverly implement Implicit Announcement removal.
803 * if( b_need_delete )
804 * RemoveAnnounce( p_sd, p_sd->p_sys->pp_announces[i]);
810 /* No need to go after six, as we start to trust the
811 * average period at six */
812 if( p_announce
->i_period_trust
<= 5 )
813 p_announce
->i_period_trust
++;
815 /* Compute the average period */
816 mtime_t now
= mdate();
817 p_announce
->i_period
= ( p_announce
->i_period
* (p_announce
->i_period_trust
-1) + (now
- p_announce
->i_last
) ) / p_announce
->i_period_trust
;
818 p_announce
->i_last
= now
;
826 CreateAnnounce( p_sd
, i_source
, i_hash
, p_sdp
);
835 sap_announce_t
*CreateAnnounce( services_discovery_t
*p_sd
, uint32_t *i_source
, uint16_t i_hash
,
838 input_item_t
*p_input
;
839 const char *psz_value
;
840 sap_announce_t
*p_sap
= (sap_announce_t
*)malloc(
841 sizeof(sap_announce_t
) );
842 services_discovery_sys_t
*p_sys
;
848 p_sap
->i_last
= mdate();
850 p_sap
->i_period_trust
= 0;
851 p_sap
->i_hash
= i_hash
;
852 memcpy (p_sap
->i_source
, i_source
, sizeof(p_sap
->i_source
));
853 p_sap
->p_sdp
= p_sdp
;
855 /* Released in RemoveAnnounce */
856 p_input
= input_item_NewWithType( p_sap
->p_sdp
->psz_uri
,
857 p_sdp
->psz_sessionname
,
858 0, NULL
, 0, -1, ITEM_TYPE_NET
);
859 if( unlikely(p_input
== NULL
) )
864 p_sap
->p_item
= p_input
;
866 vlc_meta_t
*p_meta
= vlc_meta_New();
867 if( likely(p_meta
!= NULL
) )
869 vlc_meta_Set( p_meta
, vlc_meta_Description
, p_sdp
->psz_sessioninfo
);
870 p_input
->p_meta
= p_meta
;
873 if( p_sdp
->rtcp_port
)
876 if( asprintf( &rtcp
, ":rtcp-port=%u", p_sdp
->rtcp_port
) != -1 )
878 input_item_AddOption( p_input
, rtcp
, VLC_INPUT_OPTION_TRUSTED
);
883 psz_value
= GetAttribute( p_sap
->p_sdp
->pp_attributes
, p_sap
->p_sdp
->i_attributes
, "tool" );
884 if( psz_value
!= NULL
)
886 input_item_AddInfo( p_input
, _("Session"), _("Tool"), "%s", psz_value
);
888 if( strcmp( p_sdp
->username
, "-" ) )
890 input_item_AddInfo( p_input
, _("Session"), _("User"), "%s",
894 /* Handle category */
895 psz_value
= GetAttribute(p_sap
->p_sdp
->pp_attributes
,
896 p_sap
->p_sdp
->i_attributes
, "cat");
897 if (psz_value
!= NULL
)
899 /* a=cat provides a dot-separated hierarchy.
900 * For the time being only replace dots with pipe. TODO: FIXME */
901 char *str
= strdup(psz_value
);
902 if (likely(str
!= NULL
))
903 for (char *p
= strchr(str
, '.'); p
!= NULL
; p
= strchr(p
, '.'))
905 services_discovery_AddItem(p_sd
, p_input
, str
? str
: psz_value
);
910 /* backward compatibility with VLC 0.7.3-2.0.0 senders */
911 psz_value
= GetAttribute(p_sap
->p_sdp
->pp_attributes
,
912 p_sap
->p_sdp
->i_attributes
, "x-plgroup");
913 services_discovery_AddItem(p_sd
, p_input
, psz_value
);
916 TAB_APPEND( p_sys
->i_announces
, p_sys
->pp_announces
, p_sap
);
922 static const char *FindAttribute (const sdp_t
*sdp
, unsigned media
,
925 /* Look for media attribute, and fallback to session */
926 const char *attr
= GetAttribute (sdp
->mediav
[media
].pp_attributes
,
927 sdp
->mediav
[media
].i_attributes
, name
);
929 attr
= GetAttribute (sdp
->pp_attributes
, sdp
->i_attributes
, name
);
934 /* Fill p_sdp->psz_uri */
935 static int ParseConnection( vlc_object_t
*p_obj
, sdp_t
*p_sdp
)
937 if (p_sdp
->mediac
== 0)
939 msg_Dbg (p_obj
, "Ignoring SDP with no media");
943 for (unsigned i
= 1; i
< p_sdp
->mediac
; i
++)
945 if ((p_sdp
->mediav
[i
].n_addr
!= p_sdp
->mediav
->n_addr
)
946 || (p_sdp
->mediav
[i
].addrlen
!= p_sdp
->mediav
->addrlen
)
947 || memcmp (&p_sdp
->mediav
[i
].addr
, &p_sdp
->mediav
->addr
,
948 p_sdp
->mediav
->addrlen
))
950 msg_Dbg (p_obj
, "Multiple media ports not supported -> live555");
955 if (p_sdp
->mediav
->n_addr
!= 1)
957 msg_Dbg (p_obj
, "Layered encoding not supported -> live555");
966 if (vlc_getnameinfo ((struct sockaddr
*)&(p_sdp
->mediav
->addr
),
967 p_sdp
->mediav
->addrlen
, psz_uri
+ 1,
968 sizeof (psz_uri
) - 2, &port
, NI_NUMERICHOST
))
971 if (strchr (psz_uri
+ 1, ':'))
974 strcat (psz_uri
, "]");
980 char *sdp_proto
= strdup (p_sdp
->mediav
[0].fmt
);
981 if (sdp_proto
== NULL
)
984 char *subtype
= strchr (sdp_proto
, ' ');
987 msg_Dbg (p_obj
, "missing SDP media subtype: %s", sdp_proto
);
994 /* FIXME: check for multiple payload types in RTP/AVP case.
995 * FIXME: check for "mpeg" subtype in raw udp case. */
996 if (!strcasecmp (sdp_proto
, "udp"))
997 p_sdp
->i_media_type
= 33;
999 p_sdp
->i_media_type
= atoi (subtype
);
1002 /* RTP protocol, nul, VLC shortcut, nul, flags byte as follow:
1003 * 0x1: Connection-Oriented media. */
1004 static const char proto_match
[] =
1006 "RTP/AVP\0" "rtp\0\0"
1007 "UDPLite/RTP/AVP\0" "udplite\0\0"
1008 "DCCP/RTP/AVP\0" "dccp\0\1"
1009 "TCP/RTP/AVP\0" "rtptcp\0\1"
1012 const char *vlc_proto
= NULL
;
1014 for (const char *proto
= proto_match
; *proto
;)
1016 if (strcasecmp (proto
, sdp_proto
) == 0)
1018 vlc_proto
= proto
+ strlen (proto
) + 1;
1019 flags
= vlc_proto
[strlen (vlc_proto
) + 1];
1022 proto
+= strlen (proto
) + 1;
1023 proto
+= strlen (proto
) + 2;
1027 if (vlc_proto
== NULL
)
1029 msg_Dbg (p_obj
, "unknown SDP media protocol: %s",
1030 p_sdp
->mediav
[0].fmt
);
1031 return VLC_EGENERIC
;
1034 if (!strcmp (vlc_proto
, "udp") || FindAttribute (p_sdp
, 0, "rtcp-mux"))
1035 p_sdp
->rtcp_port
= 0;
1038 const char *rtcp
= FindAttribute (p_sdp
, 0, "rtcp");
1040 p_sdp
->rtcp_port
= atoi (rtcp
);
1042 if (port
& 1) /* odd port -> RTCP; next even port -> RTP */
1043 p_sdp
->rtcp_port
= port
++;
1044 else /* even port -> RTP; next odd port -> RTCP */
1045 p_sdp
->rtcp_port
= port
+ 1;
1050 /* Connection-oriented media */
1051 const char *setup
= FindAttribute (p_sdp
, 0, "setup");
1053 setup
= "active"; /* default value */
1055 if (strcmp (setup
, "actpass") && strcmp (setup
, "passive"))
1057 msg_Dbg (p_obj
, "unsupported COMEDIA mode: %s", setup
);
1058 return VLC_EGENERIC
;
1061 if (asprintf (&p_sdp
->psz_uri
, "%s://%s:%d", vlc_proto
,
1067 /* Non-connected (normally multicast) media */
1068 char psz_source
[258] = "";
1069 const char *sfilter
= FindAttribute (p_sdp
, 0, "source-filter");
1070 if (sfilter
!= NULL
)
1072 char psz_source_ip
[256];
1075 if (sscanf (sfilter
, " incl IN IP%u %*s %255s ", &ipv
,
1076 psz_source_ip
) == 2)
1078 /* According to RFC4570, FQDNs can be used for source-filters,
1079 * but -seriously- this is impractical */
1085 struct in6_addr addr
;
1086 if ((inet_pton (AF_INET6
, psz_source_ip
, &addr
) > 0)
1087 && (inet_ntop (AF_INET6
, &addr
, psz_source
+ 1,
1088 sizeof (psz_source
) - 2) != NULL
))
1090 psz_source
[0] = '[';
1091 psz_source
[strlen (psz_source
)] = ']';
1098 struct in_addr addr
;
1099 if ((inet_pton (AF_INET
, psz_source_ip
, &addr
) > 0)
1100 && (inet_ntop (AF_INET
, &addr
, psz_source
,
1101 sizeof (psz_source
)) == NULL
))
1109 if (asprintf (&p_sdp
->psz_uri
, "%s://%s@%s:%i", vlc_proto
, psz_source
,
1118 static int ParseSDPConnection (const char *str
, struct sockaddr_storage
*addr
,
1119 socklen_t
*addrlen
, unsigned *number
)
1122 unsigned fam
, n1
, n2
;
1124 int res
= sscanf (str
, "IN IP%u %59[^/]/%u/%u", &fam
, host
, &n1
, &n2
);
1132 addr
->ss_family
= AF_INET6
;
1136 *addrlen
= sizeof (struct sockaddr_in6
);
1138 if (inet_pton (AF_INET6
, host
,
1139 &((struct sockaddr_in6
*)addr
)->sin6_addr
) <= 0)
1142 *number
= (res
>= 3) ? n1
: 1;
1147 addr
->ss_family
= AF_INET
;
1151 *addrlen
= sizeof (struct sockaddr_in
);
1153 if (inet_pton (AF_INET
, host
,
1154 &((struct sockaddr_in
*)addr
)->sin_addr
) <= 0)
1157 *number
= (res
>= 4) ? n2
: 1;
1167 /***********************************************************************
1168 * ParseSDP : SDP parsing
1169 * *********************************************************************
1170 * Validate SDP and parse all fields
1171 ***********************************************************************/
1172 static sdp_t
*ParseSDP (vlc_object_t
*p_obj
, const char *psz_sdp
)
1174 if( psz_sdp
== NULL
)
1177 sdp_t
*p_sdp
= calloc (1, sizeof (*p_sdp
));
1182 struct sockaddr_storage glob_addr
;
1183 memset (&glob_addr
, 0, sizeof (glob_addr
));
1184 socklen_t glob_len
= 0;
1185 unsigned glob_count
= 1;
1188 /* TODO: use iconv and charset attribute instead of EnsureUTF8 */
1191 /* Extract one line */
1192 char *eol
= strchr (psz_sdp
, '\n');
1193 size_t linelen
= eol
? (size_t)(eol
- psz_sdp
) : strlen (psz_sdp
);
1194 char line
[linelen
+ 1];
1195 memcpy (line
, psz_sdp
, linelen
);
1196 line
[linelen
] = '\0';
1198 psz_sdp
+= linelen
+ 1;
1200 /* Remove carriage return if present */
1201 eol
= strchr (line
, '\r');
1204 linelen
= eol
- line
;
1205 line
[linelen
] = '\0';
1209 char cat
= line
[0], *data
= line
+ 2;
1210 if (!cat
|| (strchr ("vosiuepcbtrzkam", cat
) == NULL
))
1212 /* MUST ignore SDP with unknown line type */
1213 msg_Dbg (p_obj
, "unknown SDP line type: 0x%02x", (int)cat
);
1218 msg_Dbg (p_obj
, "invalid SDP line: %s", line
);
1222 assert (linelen
>= 2);
1224 /* SDP parsing state machine
1225 * We INTERNALLY use uppercase for session, lowercase for media
1229 /* Session description */
1234 msg_Dbg (p_obj
, "missing SDP version");
1237 if (strcmp (data
, "0"))
1239 msg_Dbg (p_obj
, "unknown SDP version: %s", data
);
1249 msg_Dbg (p_obj
, "missing SDP originator");
1253 if ((sscanf (data
, "%63s %"SCNu64
" %"SCNu64
" IN IP%u %1023s",
1254 p_sdp
->username
, &p_sdp
->session_id
,
1255 &p_sdp
->session_version
, &p_sdp
->orig_ip_version
,
1256 p_sdp
->orig_host
) != 5)
1257 || ((p_sdp
->orig_ip_version
!= 4)
1258 && (p_sdp
->orig_ip_version
!= 6)))
1260 msg_Dbg (p_obj
, "SDP origin not supported: %s", data
);
1261 /* Or maybe out-of-range, but this looks suspicious */
1264 EnsureUTF8 (p_sdp
->orig_host
);
1271 if ((cat
!= 's') || !*data
)
1273 /* MUST be present AND non-empty */
1274 msg_Dbg (p_obj
, "missing SDP session name");
1277 assert (p_sdp
->psz_sessionname
== NULL
); // no memleak here
1278 p_sdp
->psz_sessionname
= strdup (data
);
1279 if (p_sdp
->psz_sessionname
== NULL
)
1281 EnsureUTF8 (p_sdp
->psz_sessionname
);
1288 /* optional (and may be empty) */
1291 assert (p_sdp
->psz_sessioninfo
== NULL
);
1292 p_sdp
->psz_sessioninfo
= strdup (data
);
1293 if (p_sdp
->psz_sessioninfo
== NULL
)
1295 EnsureUTF8 (p_sdp
->psz_sessioninfo
);
1316 if (ParseSDPConnection (data
, &glob_addr
, &glob_len
,
1319 msg_Dbg (p_obj
, "SDP connection infos not supported: "
1326 assert (expect
== 'B');
1333 msg_Dbg (p_obj
, "missing SDP time description");
1339 if ((cat
== 't') || (cat
== 'r'))
1354 attribute_t
*p_attr
= MakeAttribute (data
);
1355 TAB_APPEND( p_sdp
->i_attributes
, p_sdp
->pp_attributes
, p_attr
);
1359 /* Media description */
1366 msg_Dbg (p_obj
, "missing SDP media description");
1369 struct sdp_media_t
*m
;
1370 m
= realloc (p_sdp
->mediav
, (p_sdp
->mediac
+ 1) * sizeof (*m
));
1378 memset (m
, 0, sizeof (*m
));
1379 memcpy (&m
->addr
, &glob_addr
, m
->addrlen
= glob_len
);
1380 m
->n_addr
= glob_count
;
1382 /* TODO: remember media type (if we need multiple medias) */
1383 data
= strchr (data
, ' ');
1386 msg_Dbg (p_obj
, "missing SDP media port");
1389 port
= atoi (++data
);
1390 if (port
<= 0 || port
>= 65536)
1392 msg_Dbg (p_obj
, "invalid transport port %d", port
);
1395 net_SetPort ((struct sockaddr
*)&m
->addr
, htons (port
));
1397 data
= strchr (data
, ' ');
1400 msg_Dbg (p_obj
, "missing SDP media format");
1403 m
->fmt
= strdup (++data
);
1417 struct sdp_media_t
*m
= p_sdp
->mediav
+ p_sdp
->mediac
- 1;
1418 if (ParseSDPConnection (data
, &m
->addr
, &m
->addrlen
,
1421 msg_Dbg (p_obj
, "SDP connection infos not supported: "
1425 net_SetPort ((struct sockaddr
*)&m
->addr
, htons (port
));
1437 assert (expect
== 'a');
1440 attribute_t
*p_attr
= MakeAttribute (data
);
1444 TAB_APPEND (p_sdp
->mediav
[p_sdp
->mediac
- 1].i_attributes
,
1445 p_sdp
->mediav
[p_sdp
->mediac
- 1].pp_attributes
, p_attr
);
1452 msg_Dbg (p_obj
, "unexpected SDP line: 0x%02x", (int)cat
);
1456 msg_Err (p_obj
, "*** BUG in SDP parser! ***");
1468 static int InitSocket( services_discovery_t
*p_sd
, const char *psz_address
,
1471 int i_fd
= net_ListenUDP1 ((vlc_object_t
*)p_sd
, psz_address
, i_port
);
1473 return VLC_EGENERIC
;
1475 shutdown( i_fd
, SHUT_WR
);
1476 INSERT_ELEM (p_sd
->p_sys
->pi_fd
, p_sd
->p_sys
->i_fd
,
1477 p_sd
->p_sys
->i_fd
, i_fd
);
1481 static int Decompress( const unsigned char *psz_src
, unsigned char **_dst
, int i_len
)
1484 int i_result
, i_dstsize
, n
= 0;
1485 unsigned char *psz_dst
= NULL
;
1488 memset (&d_stream
, 0, sizeof (d_stream
));
1490 i_result
= inflateInit(&d_stream
);
1491 if( i_result
!= Z_OK
)
1494 d_stream
.next_in
= (Bytef
*)psz_src
;
1495 d_stream
.avail_in
= i_len
;
1500 psz_dst
= (unsigned char *)realloc( psz_dst
, n
* 1000 );
1501 d_stream
.next_out
= (Bytef
*)&psz_dst
[(n
- 1) * 1000];
1502 d_stream
.avail_out
= 1000;
1504 i_result
= inflate(&d_stream
, Z_NO_FLUSH
);
1505 if( ( i_result
!= Z_OK
) && ( i_result
!= Z_STREAM_END
) )
1507 inflateEnd( &d_stream
);
1512 while( ( d_stream
.avail_out
== 0 ) && ( d_stream
.avail_in
!= 0 ) &&
1513 ( i_result
!= Z_STREAM_END
) );
1515 i_dstsize
= d_stream
.total_out
;
1516 inflateEnd( &d_stream
);
1518 *_dst
= (unsigned char *)realloc( psz_dst
, i_dstsize
);
1530 static void FreeSDP( sdp_t
*p_sdp
)
1532 free( p_sdp
->psz_sessionname
);
1533 free( p_sdp
->psz_sessioninfo
);
1534 free( p_sdp
->psz_uri
);
1536 for (unsigned j
= 0; j
< p_sdp
->mediac
; j
++)
1538 free (p_sdp
->mediav
[j
].fmt
);
1539 for (int i
= 0; i
< p_sdp
->mediav
[j
].i_attributes
; i
++)
1540 FreeAttribute (p_sdp
->mediav
[j
].pp_attributes
[i
]);
1541 free (p_sdp
->mediav
[j
].pp_attributes
);
1543 free (p_sdp
->mediav
);
1545 for (int i
= 0; i
< p_sdp
->i_attributes
; i
++)
1546 FreeAttribute (p_sdp
->pp_attributes
[i
]);
1548 free (p_sdp
->pp_attributes
);
1552 static int RemoveAnnounce( services_discovery_t
*p_sd
,
1553 sap_announce_t
*p_announce
)
1557 if( p_announce
->p_sdp
)
1559 FreeSDP( p_announce
->p_sdp
);
1560 p_announce
->p_sdp
= NULL
;
1563 if( p_announce
->p_item
)
1565 services_discovery_RemoveItem( p_sd
, p_announce
->p_item
);
1566 vlc_gc_decref( p_announce
->p_item
);
1567 p_announce
->p_item
= NULL
;
1570 for( i
= 0; i
< p_sd
->p_sys
->i_announces
; i
++)
1572 if( p_sd
->p_sys
->pp_announces
[i
] == p_announce
)
1574 REMOVE_ELEM( p_sd
->p_sys
->pp_announces
, p_sd
->p_sys
->i_announces
,
1586 * Compare two sessions, when hash is not set (SAP v0)
1588 static bool IsSameSession( sdp_t
*p_sdp1
, sdp_t
*p_sdp2
)
1590 /* A session is identified by
1593 * - network type (which is always IN),
1594 * - address type (currently, this means IP version),
1597 if (strcmp (p_sdp1
->username
, p_sdp2
->username
)
1598 || (p_sdp1
->session_id
!= p_sdp2
->session_id
)
1599 || (p_sdp1
->orig_ip_version
!= p_sdp2
->orig_ip_version
)
1600 || strcmp (p_sdp1
->orig_host
, p_sdp2
->orig_host
))
1606 static inline attribute_t
*MakeAttribute (const char *str
)
1608 attribute_t
*a
= malloc (sizeof (*a
) + strlen (str
) + 1);
1612 strcpy (a
->name
, str
);
1613 EnsureUTF8 (a
->name
);
1614 char *value
= strchr (a
->name
, ':');
1626 static const char *GetAttribute (attribute_t
**tab
, unsigned n
,
1629 for (unsigned i
= 0; i
< n
; i
++)
1630 if (strcasecmp (tab
[i
]->name
, name
) == 0)
1631 return tab
[i
]->value
;
1636 static inline void FreeAttribute (attribute_t
*a
)