1 /*****************************************************************************
2  * sap.c :  SAP interface module
3  *****************************************************************************
4  * Copyright (C) 2004-2005 the VideoLAN team
5  * Copyright © 2007 Rémi Denis-Courmont
6  * $Id: 0f28dbf1b5ec4c025f79fcdae77b1ff942f774b4 $
7  *
8  * Authors: Clément Stenac <zorglub@videolan.org>
9  *          Rémi Denis-Courmont
10  *
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.
15  *
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.
20  *
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  *****************************************************************************/
25 
26 /*****************************************************************************
27  * Includes
28  *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32 
33 #define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <assert.h>
37 
38 #include <vlc_demux.h>
39 #include <vlc_services_discovery.h>
40 
41 #include <vlc_network.h>
42 #include <vlc_charset.h>
43 
44 #include <errno.h>
45 #include <unistd.h>
46 #ifdef HAVE_ARPA_INET_H
47 # include <arpa/inet.h>
48 #endif
49 #ifdef HAVE_POLL
50 # include <poll.h>
51 #endif
52 
53 #ifdef HAVE_ZLIB_H
54 #   include <zlib.h>
55 #endif
56 
57 #ifndef _WIN32
58 #   include <net/if.h>
59 #endif
60 
61 /************************************************************************
62  * Macros and definitions
63  ************************************************************************/
64 
65 #define MAX_LINE_LENGTH 256
66 
67 /* SAP is always on that port */
68 #define SAP_PORT 9875
69 /* Global-scope SAP address */
70 #define SAP_V4_GLOBAL_ADDRESS   "224.2.127.254"
71 /* Organization-local SAP address */
72 #define SAP_V4_ORG_ADDRESS      "239.195.255.255"
73 /* Local (smallest non-link-local scope) SAP address */
74 #define SAP_V4_LOCAL_ADDRESS    "239.255.255.255"
75 /* Link-local SAP address */
76 #define SAP_V4_LINK_ADDRESS     "224.0.0.255"
77 #define ADD_SESSION 1
78 
79 /*****************************************************************************
80  * Module descriptor
81  *****************************************************************************/
82 #define SAP_ADDR_TEXT N_( "SAP multicast address" )
83 #define SAP_ADDR_LONGTEXT N_( "The SAP module normally chooses itself the " \
84                               "right addresses to listen to. However, you " \
85                               "can specify a specific address." )
86 #define SAP_TIMEOUT_TEXT N_( "SAP timeout (seconds)" )
87 #define SAP_TIMEOUT_LONGTEXT N_( \
88        "Delay after which SAP items get deleted if no new announcement " \
89        "is received." )
90 #define SAP_PARSE_TEXT N_( "Try to parse the announce" )
91 #define SAP_PARSE_LONGTEXT N_( \
92        "This enables actual parsing of the announces by the SAP module. " \
93        "Otherwise, all announcements are parsed by the \"live555\" " \
94        "(RTP/RTSP) module." )
95 #define SAP_STRICT_TEXT N_( "SAP Strict mode" )
96 #define SAP_STRICT_LONGTEXT N_( \
97        "When this is set, the SAP parser will discard some non-compliant " \
98        "announcements." )
99 
100 /* Callbacks */
101     static int  Open ( vlc_object_t * );
102     static void Close( vlc_object_t * );
103     static int  OpenDemux ( vlc_object_t * );
104     static void CloseDemux ( vlc_object_t * );
105 
106 VLC_SD_PROBE_HELPER("sap", N_("Network streams (SAP)"), SD_CAT_LAN)
107 
108 vlc_module_begin ()
109     set_shortname( N_("SAP"))
110     set_description( N_("Network streams (SAP)") )
111     set_category( CAT_PLAYLIST )
112     set_subcategory( SUBCAT_PLAYLIST_SD )
113 
114     add_string( "sap-addr", NULL,
115                 SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, true )
116     add_obsolete_bool( "sap-ipv4" ) /* since 2.0.0 */
117     add_obsolete_bool( "sap-ipv6" ) /* since 2.0.0 */
118     add_integer( "sap-timeout", 1800,
119                  SAP_TIMEOUT_TEXT, SAP_TIMEOUT_LONGTEXT, true )
120     add_bool( "sap-parse", true,
121                SAP_PARSE_TEXT,SAP_PARSE_LONGTEXT, true )
122     add_bool( "sap-strict", false,
123                SAP_STRICT_TEXT,SAP_STRICT_LONGTEXT, true )
124     add_obsolete_bool( "sap-timeshift" ) /* Redumdant since 1.0.0 */
125 
126     set_capability( "services_discovery", 0 )
127     set_callbacks( Open, Close )
128 
129     VLC_SD_PROBE_SUBMODULE
130 
131     add_submodule ()
132         set_description( N_("SDP Descriptions parser") )
133         add_shortcut( "sdp" )
134         set_capability( "demux", 51 )
135         set_callbacks( OpenDemux, CloseDemux )
136 vlc_module_end ()
137 
138 
139 /*****************************************************************************
140  * Local structures
141  *****************************************************************************/
142 
143 typedef struct sdp_t sdp_t;
144 typedef struct attribute_t attribute_t;
145 typedef struct sap_announce_t sap_announce_t;
146 
147 
148 struct sdp_media_t
149 {
150     struct sdp_t           *parent;
151     char                   *fmt;
152     struct sockaddr_storage addr;
153     socklen_t               addrlen;
154     unsigned                n_addr;
155     int           i_attributes;
156     attribute_t  **pp_attributes;
157 };
158 
159 
160 /* The structure that contains sdp information */
161 struct  sdp_t
162 {
163     const char *psz_sdp;
164 
165     /* o field */
166     char     username[64];
167     uint64_t session_id;
168     uint64_t session_version;
169     unsigned orig_ip_version;
170     char     orig_host[1024];
171 
172     /* s= field */
173     char *psz_sessionname;
174 
175     /* i= field */
176     char *psz_sessioninfo;
177 
178     /* old cruft */
179     /* "computed" URI */
180     char *psz_uri;
181     int           i_media_type;
182     unsigned rtcp_port;
183 
184     /* a= global attributes */
185     int           i_attributes;
186     attribute_t  **pp_attributes;
187 
188     /* medias (well, we only support one atm) */
189     unsigned            mediac;
190     struct sdp_media_t *mediav;
191 };
192 
193 struct attribute_t
194 {
195     const char *value;
196     char name[];
197 };
198 
199 struct sap_announce_t
200 {
201     mtime_t i_last;
202     mtime_t i_period;
203     uint8_t i_period_trust;
204 
205     uint16_t    i_hash;
206     uint32_t    i_source[4];
207 
208     /* SAP annnounces must only contain one SDP */
209     sdp_t       *p_sdp;
210 
211     input_item_t * p_item;
212 };
213 
214 struct services_discovery_sys_t
215 {
216     vlc_thread_t thread;
217 
218     /* Socket descriptors */
219     int i_fd;
220     int *pi_fd;
221 
222     /* Table of announces */
223     int i_announces;
224     struct sap_announce_t **pp_announces;
225 
226     /* Modes */
227     bool  b_strict;
228     bool  b_parse;
229 
230     int i_timeout;
231 };
232 
233 struct demux_sys_t
234 {
235     sdp_t *p_sdp;
236 };
237 
238 /*****************************************************************************
239  * Local prototypes
240  *****************************************************************************/
241 
242 
243 /* Main functions */
244     static int Demux( demux_t *p_demux );
245     static int Control( demux_t *, int, va_list );
246     static void *Run  ( void *p_sd );
247 
248 /* Main parsing functions */
249     static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp );
250     static int ParseSAP( services_discovery_t *p_sd, const uint8_t *p_buffer, size_t i_read );
251     static sdp_t *ParseSDP (vlc_object_t *p_sd, const char *psz_sdp);
252     static sap_announce_t *CreateAnnounce( services_discovery_t *, uint32_t *, uint16_t, sdp_t * );
253     static int RemoveAnnounce( services_discovery_t *p_sd, sap_announce_t *p_announce );
254 
255 /* Helper functions */
256     static inline attribute_t *MakeAttribute (const char *str);
257     static const char *GetAttribute (attribute_t **tab, unsigned n, const char *name);
258     static inline void FreeAttribute (attribute_t *a);
259     static const char *FindAttribute (const sdp_t *sdp, unsigned media,
260                                       const char *name);
261 
262     static bool IsSameSession( sdp_t *p_sdp1, sdp_t *p_sdp2 );
263     static int InitSocket( services_discovery_t *p_sd, const char *psz_address, int i_port );
264     static int Decompress( const unsigned char *psz_src, unsigned char **_dst, int i_len );
265     static void FreeSDP( sdp_t *p_sdp );
266 
min_int(int a,int b)267 static inline int min_int( int a, int b )
268 {
269     return a > b ? b : a;
270 }
271 
IsWellKnownPayload(int type)272 static bool IsWellKnownPayload (int type)
273 {
274     switch (type)
275     {   /* Should be in sync with modules/demux/rtp.c */
276         case  0: /* PCMU/8000 */
277         case  3:
278         case  8: /* PCMA/8000 */
279         case 10: /* L16/44100/2 */
280         case 11: /* L16/44100 */
281         case 12:
282         case 14: /* MPA/90000 */
283         case 32: /* MPV/90000 */
284         case 33: /* MP2/90000 */
285             return true;
286    }
287    return false;
288 }
289 
290 /*****************************************************************************
291  * Open: initialize and create stuff
292  *****************************************************************************/
Open(vlc_object_t * p_this)293 static int Open( vlc_object_t *p_this )
294 {
295     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
296     services_discovery_sys_t *p_sys  = (services_discovery_sys_t *)
297                                 malloc( sizeof( services_discovery_sys_t ) );
298     if( !p_sys )
299         return VLC_ENOMEM;
300 
301     p_sys->i_timeout = var_CreateGetInteger( p_sd, "sap-timeout" );
302 
303     p_sd->p_sys  = p_sys;
304     p_sd->description = _("Network streams (SAP)");
305 
306     p_sys->pi_fd = NULL;
307     p_sys->i_fd = 0;
308 
309     p_sys->b_strict = var_CreateGetBool( p_sd, "sap-strict");
310     p_sys->b_parse = var_CreateGetBool( p_sd, "sap-parse" );
311 
312     p_sys->i_announces = 0;
313     p_sys->pp_announces = NULL;
314     /* TODO: create sockets here, and fix racy sockets table */
315     if (vlc_clone (&p_sys->thread, Run, p_sd, VLC_THREAD_PRIORITY_LOW))
316     {
317         free (p_sys);
318         return VLC_EGENERIC;
319     }
320 
321     return VLC_SUCCESS;
322 }
323 
324 /*****************************************************************************
325  * OpenDemux: initialize and create stuff
326  *****************************************************************************/
OpenDemux(vlc_object_t * p_this)327 static int OpenDemux( vlc_object_t *p_this )
328 {
329     demux_t *p_demux = (demux_t *)p_this;
330     const uint8_t *p_peek;
331     char *psz_sdp = NULL;
332     sdp_t *p_sdp = NULL;
333     int errval = VLC_EGENERIC;
334     size_t i_len;
335 
336     if( !var_CreateGetBool( p_demux, "sap-parse" ) )
337     {
338         /* We want livedotcom module to parse this SDP file */
339         return VLC_EGENERIC;
340     }
341 
342     assert( p_demux->s ); /* this is NOT an access_demux */
343 
344     /* Probe for SDP */
345     if( vlc_stream_Peek( p_demux->s, &p_peek, 7 ) < 7 )
346         return VLC_EGENERIC;
347 
348     if( memcmp( p_peek, "v=0\r\no=", 7 ) && memcmp( p_peek, "v=0\no=", 6 ) )
349         return VLC_EGENERIC;
350 
351     /* Gather the complete sdp file */
352     for( i_len = 0, psz_sdp = NULL; i_len < 65536; )
353     {
354         const int i_read_max = 1024;
355         char *psz_sdp_new = realloc( psz_sdp, i_len + i_read_max + 1 );
356         size_t i_read;
357         if( psz_sdp_new == NULL )
358         {
359             errval = VLC_ENOMEM;
360             goto error;
361         }
362         psz_sdp = psz_sdp_new;
363 
364         i_read = vlc_stream_Read( p_demux->s, &psz_sdp[i_len], i_read_max );
365         if( (int)i_read < 0 )
366         {
367             msg_Err( p_demux, "cannot read SDP" );
368             goto error;
369         }
370         i_len += i_read;
371 
372         psz_sdp[i_len] = '\0';
373 
374         if( (int)i_read < i_read_max )
375             break; // EOF
376     }
377 
378     p_sdp = ParseSDP( VLC_OBJECT(p_demux), psz_sdp );
379 
380     if( !p_sdp )
381     {
382         msg_Warn( p_demux, "invalid SDP");
383         goto error;
384     }
385 
386     if( ParseConnection( VLC_OBJECT( p_demux ), p_sdp ) )
387     {
388         p_sdp->psz_uri = NULL;
389     }
390     if (!IsWellKnownPayload (p_sdp->i_media_type))
391         goto error;
392     if( p_sdp->psz_uri == NULL ) goto error;
393 
394     p_demux->p_sys = (demux_sys_t *)malloc( sizeof(demux_sys_t) );
395     if( unlikely( !p_demux->p_sys ) )
396         goto error;
397     p_demux->p_sys->p_sdp = p_sdp;
398     p_demux->pf_control = Control;
399     p_demux->pf_demux = Demux;
400 
401     FREENULL( psz_sdp );
402     return VLC_SUCCESS;
403 
404 error:
405     FREENULL( psz_sdp );
406     if( p_sdp ) FreeSDP( p_sdp );
407     return errval;
408 }
409 
410 /*****************************************************************************
411  * Close:
412  *****************************************************************************/
Close(vlc_object_t * p_this)413 static void Close( vlc_object_t *p_this )
414 {
415     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
416     services_discovery_sys_t    *p_sys  = p_sd->p_sys;
417     int i;
418 
419     vlc_cancel (p_sys->thread);
420     vlc_join (p_sys->thread, NULL);
421 
422     for( i = p_sys->i_fd-1 ; i >= 0 ; i-- )
423     {
424         net_Close( p_sys->pi_fd[i] );
425     }
426     FREENULL( p_sys->pi_fd );
427 
428     for( i = p_sys->i_announces  - 1;  i>= 0; i-- )
429     {
430         RemoveAnnounce( p_sd, p_sys->pp_announces[i] );
431     }
432     FREENULL( p_sys->pp_announces );
433 
434     free( p_sys );
435 }
436 
437 /*****************************************************************************
438  * CloseDemux: Close the demuxer
439  *****************************************************************************/
CloseDemux(vlc_object_t * p_this)440 static void CloseDemux( vlc_object_t *p_this )
441 {
442     demux_t *p_demux = (demux_t *)p_this;
443 
444     if( p_demux->p_sys->p_sdp )
445         FreeSDP( p_demux->p_sys->p_sdp );
446     free( p_demux->p_sys );
447 }
448 
449 /*****************************************************************************
450  * Run: main SAP thread
451  *****************************************************************************
452  * Listens to SAP packets, and sends them to packet_handle
453  *****************************************************************************/
454 #define MAX_SAP_BUFFER 5000
455 
Run(void * data)456 static void *Run( void *data )
457 {
458     services_discovery_t *p_sd = data;
459     char *psz_addr;
460     int timeout = -1;
461     int canc = vlc_savecancel ();
462 
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 );
473 
474     char psz_address[NI_MAXNUMERICHOST] = "ff02::2:7ffe%";
475 #ifndef _WIN32
476     struct if_nameindex *l = if_nameindex ();
477     if (l != NULL)
478     {
479         char *ptr = strchr (psz_address, '%') + 1;
480         for (unsigned i = 0; l[i].if_index; i++)
481         {
482             strcpy (ptr, l[i].if_name);
483             InitSocket (p_sd, psz_address, SAP_PORT);
484         }
485         if_freenameindex (l);
486     }
487 #else
488         /* this is the Winsock2 equivalant of SIOCGIFCONF on BSD stacks,
489            which if_nameindex uses internally anyway */
490 
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 )
494         {
495             INTERFACE_INFO ifaces[10]; // Assume there will be no more than 10 IP interfaces
496             DWORD len = sizeof(ifaces);
497 
498             if( SOCKET_ERROR != WSAIoctl(s, SIO_GET_INTERFACE_LIST, NULL, 0, &ifaces, len, &len, NULL, NULL) )
499             {
500                 unsigned ifcount = len/sizeof(INTERFACE_INFO);
501                 char *ptr = strchr (psz_address, '%') + 1;
502                 for(unsigned i = 1; i<=ifcount; ++i )
503                 {
504                     // append link-local zone identifier
505                     sprintf(ptr, "%d", i);
506                 }
507             }
508             closesocket(s);
509         }
510 #endif
511     *strchr (psz_address, '%') = '\0';
512 
513     static const char ipv6_scopes[] = "1456789ABCDE";
514     for (const char *c_scope = ipv6_scopes; *c_scope; c_scope++)
515     {
516         psz_address[3] = *c_scope;
517         InitSocket( p_sd, psz_address, SAP_PORT );
518     }
519 
520     psz_addr = var_CreateGetString( p_sd, "sap-addr" );
521     if( psz_addr && *psz_addr )
522         InitSocket( p_sd, psz_addr, SAP_PORT );
523     free( psz_addr );
524 
525     if( p_sd->p_sys->i_fd == 0 )
526     {
527         msg_Err( p_sd, "unable to listen on any address" );
528         return NULL;
529     }
530 
531     /* read SAP packets */
532     for (;;)
533     {
534         vlc_restorecancel (canc);
535         unsigned n = p_sd->p_sys->i_fd;
536         struct pollfd ufd[n];
537 
538         for (unsigned i = 0; i < n; i++)
539         {
540             ufd[i].fd = p_sd->p_sys->pi_fd[i];
541             ufd[i].events = POLLIN;
542             ufd[i].revents = 0;
543         }
544 
545         int val = poll (ufd, n, timeout);
546         canc = vlc_savecancel ();
547         if (val > 0)
548         {
549             for (unsigned i = 0; i < n; i++)
550             {
551                 if (ufd[i].revents)
552                 {
553                     uint8_t p_buffer[MAX_SAP_BUFFER+1];
554                     ssize_t i_read;
555 
556                     i_read = recv (ufd[i].fd, p_buffer, MAX_SAP_BUFFER, 0);
557                     if (i_read < 0)
558                         msg_Warn (p_sd, "receive error: %s",
559                                   vlc_strerror_c(errno));
560                     if (i_read > 6)
561                     {
562                         /* Parse the packet */
563                         p_buffer[i_read] = '\0';
564                         ParseSAP (p_sd, p_buffer, i_read);
565                     }
566                 }
567             }
568         }
569 
570         mtime_t now = mdate();
571 
572         /* A 1 hour timeout correspond to the RFC Implicit timeout.
573          * This timeout is tuned in the following loop. */
574         timeout = 1000 * 60 * 60;
575 
576         /* Check for items that need deletion */
577         for( int i = 0; i < p_sd->p_sys->i_announces; i++ )
578         {
579             mtime_t i_timeout = ( mtime_t ) 1000000 * p_sd->p_sys->i_timeout;
580             sap_announce_t * p_announce = p_sd->p_sys->pp_announces[i];
581             mtime_t i_last_period = now - p_announce->i_last;
582 
583             /* Remove the announcement, if the last announcement was 1 hour ago
584              * or if the last packet emitted was 10 times the average time
585              * between two packets */
586             if( ( p_announce->i_period_trust > 5 && i_last_period > 10 * p_announce->i_period ) ||
587                 i_last_period > i_timeout )
588             {
589                 RemoveAnnounce( p_sd, p_announce );
590             }
591             else
592             {
593                 /* Compute next timeout */
594                 if( p_announce->i_period_trust > 5 )
595                     timeout = min_int((10 * p_announce->i_period - i_last_period) / 1000, timeout);
596                 timeout = min_int((i_timeout - i_last_period)/1000, timeout);
597             }
598         }
599 
600         if( !p_sd->p_sys->i_announces )
601             timeout = -1; /* We can safely poll indefinitely. */
602         else if( timeout < 200 )
603             timeout = 200; /* Don't wakeup too fast. */
604     }
605     vlc_assert_unreachable ();
606 }
607 
608 /**********************************************************************
609  * Demux: reads and demuxes data packets
610  * Return -1 if error, 0 if EOF, 1 else
611  **********************************************************************/
Demux(demux_t * p_demux)612 static int Demux( demux_t *p_demux )
613 {
614     sdp_t *p_sdp = p_demux->p_sys->p_sdp;
615     input_thread_t *p_input = p_demux->p_input;
616     input_item_t *p_parent_input;
617 
618     if( !p_input )
619     {
620         msg_Err( p_demux, "parent input could not be found" );
621         return VLC_EGENERIC;
622     }
623 
624     /* This item hasn't been held by input_GetItem
625      * don't release it */
626     p_parent_input = input_GetItem( p_input );
627 
628     input_item_SetURI( p_parent_input, p_sdp->psz_uri );
629     input_item_SetName( p_parent_input, p_sdp->psz_sessionname );
630     if( p_sdp->rtcp_port )
631     {
632         char *rtcp;
633         if( asprintf( &rtcp, ":rtcp-port=%u", p_sdp->rtcp_port ) != -1 )
634         {
635             input_item_AddOption( p_parent_input, rtcp, VLC_INPUT_OPTION_TRUSTED );
636             free( rtcp );
637         }
638     }
639 
640     vlc_mutex_lock( &p_parent_input->lock );
641 
642     p_parent_input->i_type = ITEM_TYPE_STREAM;
643     p_parent_input->b_net = true;
644 
645     vlc_mutex_unlock( &p_parent_input->lock );
646     return VLC_SUCCESS;
647 }
648 
Control(demux_t * p_demux,int i_query,va_list args)649 static int Control( demux_t *p_demux, int i_query, va_list args )
650 {
651     VLC_UNUSED(p_demux); VLC_UNUSED(i_query); VLC_UNUSED(args);
652     return VLC_EGENERIC;
653 }
654 
655 /**************************************************************
656  * Local functions
657  **************************************************************/
658 
659 /* i_read is at least > 6 */
ParseSAP(services_discovery_t * p_sd,const uint8_t * buf,size_t len)660 static int ParseSAP( services_discovery_t *p_sd, const uint8_t *buf,
661                      size_t len )
662 {
663     const char          *psz_sdp;
664     const uint8_t *end = buf + len;
665     sdp_t               *p_sdp;
666     uint32_t            i_source[4];
667 
668     assert (buf[len] == '\0');
669 
670     if (len < 4)
671         return VLC_EGENERIC;
672 
673     uint8_t flags = buf[0];
674     uint8_t auth_len = buf[1];
675 
676     /* First, check the sap announce is correct */
677     if ((flags >> 5) != 1)
678         return VLC_EGENERIC;
679 
680     bool b_ipv6 = (flags & 0x10) != 0;
681     bool b_need_delete = (flags & 0x04) != 0;
682 
683     if (flags & 0x02)
684     {
685         msg_Dbg( p_sd, "encrypted packet, unsupported" );
686         return VLC_EGENERIC;
687     }
688 
689     bool b_compressed = (flags & 0x01) != 0;
690 
691     uint16_t i_hash = U16_AT (buf + 2);
692 
693     if( p_sd->p_sys->b_strict && i_hash == 0 )
694     {
695         msg_Dbg( p_sd, "strict mode, discarding announce with null id hash");
696         return VLC_EGENERIC;
697     }
698 
699     buf += 4;
700     if( b_ipv6 )
701     {
702         for( int i = 0; i < 4; i++,buf+=4)
703             i_source[i] = U32_AT(buf);
704     }
705     else
706     {
707         memset(i_source, 0, sizeof(i_source));
708         i_source[3] = U32_AT(buf);
709         buf+=4;
710     }
711     // Skips auth data
712     buf += auth_len;
713     if (buf > end)
714         return VLC_EGENERIC;
715 
716     uint8_t *decomp = NULL;
717     if( b_compressed )
718     {
719         int newsize = Decompress (buf, &decomp, end - buf);
720         if (newsize < 0)
721         {
722             msg_Dbg( p_sd, "decompression of SAP packet failed" );
723             return VLC_EGENERIC;
724         }
725 
726         decomp = xrealloc (decomp, newsize + 1);
727         decomp[newsize] = '\0';
728         psz_sdp = (const char *)decomp;
729         len = newsize;
730     }
731     else
732     {
733         psz_sdp = (const char *)buf;
734         len = end - buf;
735     }
736 
737     /* len is a strlen here here. both buf and decomp are len+1 where the 1 should be a \0 */
738     assert( psz_sdp[len] == '\0');
739 
740     /* Skip payload type */
741     /* SAPv1 has implicit "application/sdp" payload type: first line is v=0 */
742     if (strncmp (psz_sdp, "v=0", 3))
743     {
744         size_t clen = strlen (psz_sdp) + 1;
745 
746         if (strcmp (psz_sdp, "application/sdp"))
747         {
748             msg_Dbg (p_sd, "unsupported content type: %s", psz_sdp);
749             goto error;
750         }
751 
752         // skips content type
753         if (len <= clen)
754             goto error;
755 
756         len -= clen;
757         psz_sdp += clen;
758     }
759 
760     /* Parse SDP info */
761     p_sdp = ParseSDP( VLC_OBJECT(p_sd), psz_sdp );
762 
763     if( p_sdp == NULL )
764         goto error;
765 
766     p_sdp->psz_sdp = psz_sdp;
767 
768     /* Decide whether we should add a playlist item for this SDP */
769     /* Parse connection information (c= & m= ) */
770     if( ParseConnection( VLC_OBJECT(p_sd), p_sdp ) )
771         p_sdp->psz_uri = NULL;
772 
773     /* Multi-media or no-parse -> pass to LIVE.COM */
774     if( !IsWellKnownPayload( p_sdp->i_media_type ) || !p_sd->p_sys->b_parse )
775     {
776         free( p_sdp->psz_uri );
777         if (asprintf( &p_sdp->psz_uri, "sdp://%s", p_sdp->psz_sdp ) == -1)
778             p_sdp->psz_uri = NULL;
779     }
780 
781     if( p_sdp->psz_uri == NULL )
782     {
783         FreeSDP( p_sdp );
784         goto error;
785     }
786 
787     for( int i = 0 ; i< p_sd->p_sys->i_announces ; i++ )
788     {
789         sap_announce_t * p_announce = p_sd->p_sys->pp_announces[i];
790         /* FIXME: slow */
791         if( ( !i_hash && IsSameSession( p_announce->p_sdp, p_sdp ) )
792             || ( i_hash && p_announce->i_hash == i_hash
793                  && !memcmp(p_announce->i_source, i_source, sizeof(i_source)) ) )
794         {
795             /* We don't support delete announcement as they can easily
796              * Be used to highjack an announcement by a third party.
797              * Instead we cleverly implement Implicit Announcement removal.
798              *
799              * if( b_need_delete )
800              *    RemoveAnnounce( p_sd, p_sd->p_sys->pp_announces[i]);
801              * else
802              */
803 
804             if( !b_need_delete )
805             {
806                 /* No need to go after six, as we start to trust the
807                  * average period at six */
808                 if( p_announce->i_period_trust <= 5 )
809                     p_announce->i_period_trust++;
810 
811                 /* Compute the average period */
812                 mtime_t now = mdate();
813                 p_announce->i_period = ( p_announce->i_period * (p_announce->i_period_trust-1) + (now - p_announce->i_last) ) / p_announce->i_period_trust;
814                 p_announce->i_last = now;
815             }
816             FreeSDP( p_sdp );
817             free (decomp);
818             return VLC_SUCCESS;
819         }
820     }
821 
822     CreateAnnounce( p_sd, i_source, i_hash, p_sdp );
823 
824     free (decomp);
825     return VLC_SUCCESS;
826 error:
827     free (decomp);
828     return VLC_EGENERIC;
829 }
830 
CreateAnnounce(services_discovery_t * p_sd,uint32_t * i_source,uint16_t i_hash,sdp_t * p_sdp)831 sap_announce_t *CreateAnnounce( services_discovery_t *p_sd, uint32_t *i_source, uint16_t i_hash,
832                                 sdp_t *p_sdp )
833 {
834     input_item_t *p_input;
835     const char *psz_value;
836     sap_announce_t *p_sap = (sap_announce_t *)malloc(
837                                         sizeof(sap_announce_t ) );
838     services_discovery_sys_t *p_sys;
839     if( p_sap == NULL )
840         return NULL;
841 
842     p_sys = p_sd->p_sys;
843 
844     p_sap->i_last = mdate();
845     p_sap->i_period = 0;
846     p_sap->i_period_trust = 0;
847     p_sap->i_hash = i_hash;
848     memcpy (p_sap->i_source, i_source, sizeof(p_sap->i_source));
849     p_sap->p_sdp = p_sdp;
850 
851     /* Released in RemoveAnnounce */
852     p_input = input_item_NewStream( p_sap->p_sdp->psz_uri, p_sdp->psz_sessionname,
853                                     -1 );
854     if( unlikely(p_input == NULL) )
855     {
856         free( p_sap );
857         return NULL;
858     }
859     p_sap->p_item = p_input;
860 
861     vlc_meta_t *p_meta = vlc_meta_New();
862     if( likely(p_meta != NULL) )
863     {
864         vlc_meta_Set( p_meta, vlc_meta_Description, p_sdp->psz_sessioninfo );
865         p_input->p_meta = p_meta;
866     }
867 
868     if( p_sdp->rtcp_port )
869     {
870         char *rtcp;
871         if( asprintf( &rtcp, ":rtcp-port=%u", p_sdp->rtcp_port ) != -1 )
872         {
873             input_item_AddOption( p_input, rtcp, VLC_INPUT_OPTION_TRUSTED );
874             free( rtcp );
875         }
876     }
877 
878     psz_value = GetAttribute( p_sap->p_sdp->pp_attributes, p_sap->p_sdp->i_attributes, "tool" );
879     if( psz_value != NULL )
880     {
881         input_item_AddInfo( p_input, _("Session"), _("Tool"), "%s", psz_value );
882     }
883     if( strcmp( p_sdp->username, "-" ) )
884     {
885         input_item_AddInfo( p_input, _("Session"), _("User"), "%s",
886                            p_sdp->username );
887     }
888 
889     /* Handle category */
890     psz_value = GetAttribute(p_sap->p_sdp->pp_attributes,
891                              p_sap->p_sdp->i_attributes, "cat");
892     if (psz_value != NULL)
893     {
894         /* a=cat provides a dot-separated hierarchy.
895          * For the time being only replace dots with pipe. TODO: FIXME */
896         char *str = strdup(psz_value);
897         if (likely(str != NULL))
898             for (char *p = strchr(str, '.'); p != NULL; p = strchr(p, '.'))
899                 *(p++) = '|';
900         services_discovery_AddItemCat(p_sd, p_input, str ? str : psz_value);
901         free(str);
902     }
903     else
904     {
905         /* backward compatibility with VLC 0.7.3-2.0.0 senders */
906         psz_value = GetAttribute(p_sap->p_sdp->pp_attributes,
907                                  p_sap->p_sdp->i_attributes, "x-plgroup");
908         services_discovery_AddItemCat(p_sd, p_input, psz_value);
909     }
910 
911     TAB_APPEND( p_sys->i_announces, p_sys->pp_announces, p_sap );
912 
913     return p_sap;
914 }
915 
916 
FindAttribute(const sdp_t * sdp,unsigned media,const char * name)917 static const char *FindAttribute (const sdp_t *sdp, unsigned media,
918                                   const char *name)
919 {
920     /* Look for media attribute, and fallback to session */
921     const char *attr = GetAttribute (sdp->mediav[media].pp_attributes,
922                                      sdp->mediav[media].i_attributes, name);
923     if (attr == NULL)
924         attr = GetAttribute (sdp->pp_attributes, sdp->i_attributes, name);
925     return attr;
926 }
927 
928 
929 /* Fill p_sdp->psz_uri */
ParseConnection(vlc_object_t * p_obj,sdp_t * p_sdp)930 static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp )
931 {
932     if (p_sdp->mediac == 0)
933     {
934         msg_Dbg (p_obj, "Ignoring SDP with no media");
935         return VLC_EGENERIC;
936     }
937 
938     for (unsigned i = 1; i < p_sdp->mediac; i++)
939     {
940         if ((p_sdp->mediav[i].n_addr != p_sdp->mediav->n_addr)
941          || (p_sdp->mediav[i].addrlen != p_sdp->mediav->addrlen)
942          || memcmp (&p_sdp->mediav[i].addr, &p_sdp->mediav->addr,
943                     p_sdp->mediav->addrlen))
944         {
945             msg_Dbg (p_obj, "Multiple media ports not supported -> live555");
946             return VLC_EGENERIC;
947         }
948     }
949 
950     if (p_sdp->mediav->n_addr != 1)
951     {
952         msg_Dbg (p_obj, "Layered encoding not supported -> live555");
953         return VLC_EGENERIC;
954     }
955 
956     char psz_uri[1026];
957     const char *host;
958     int port;
959 
960     psz_uri[0] = '[';
961     if (vlc_getnameinfo ((struct sockaddr *)&(p_sdp->mediav->addr),
962                          p_sdp->mediav->addrlen, psz_uri + 1,
963                          sizeof (psz_uri) - 2, &port, NI_NUMERICHOST))
964         return VLC_EGENERIC;
965 
966     if (strchr (psz_uri + 1, ':'))
967     {
968         host = psz_uri;
969         strcat (psz_uri, "]");
970     }
971     else
972         host = psz_uri + 1;
973 
974     /* Parse m= field */
975     char *sdp_proto = strdup (p_sdp->mediav[0].fmt);
976     if (sdp_proto == NULL)
977         return VLC_ENOMEM;
978 
979     char *subtype = strchr (sdp_proto, ' ');
980     if (subtype == NULL)
981     {
982         msg_Dbg (p_obj, "missing SDP media subtype: %s", sdp_proto);
983         free (sdp_proto);
984         return VLC_EGENERIC;
985     }
986     else
987     {
988         *subtype++ = '\0';
989         /* FIXME: check for multiple payload types in RTP/AVP case.
990          * FIXME: check for "mpeg" subtype in raw udp case. */
991         if (!strcasecmp (sdp_proto, "udp"))
992             p_sdp->i_media_type = 33;
993         else
994             p_sdp->i_media_type = atoi (subtype);
995     }
996 
997     /* RTP protocol, nul, VLC shortcut, nul, flags byte as follow:
998      * 0x1: Connection-Oriented media. */
999     static const char proto_match[] =
1000         "udp\0"             "udp\0\0"
1001         "RTP/AVP\0"         "rtp\0\0"
1002         "UDPLite/RTP/AVP\0" "udplite\0\0"
1003         "DCCP/RTP/AVP\0"    "dccp\0\1"
1004         "TCP/RTP/AVP\0"     "rtptcp\0\1"
1005         "\0";
1006 
1007     const char *vlc_proto = NULL;
1008     uint8_t flags = 0;
1009     for (const char *proto = proto_match; *proto;)
1010     {
1011         if (strcasecmp (proto, sdp_proto) == 0)
1012         {
1013             vlc_proto = proto + strlen (proto) + 1;
1014             flags = vlc_proto[strlen (vlc_proto) + 1];
1015             break;
1016         }
1017         proto += strlen (proto) + 1;
1018         proto += strlen (proto) + 2;
1019     }
1020 
1021     free (sdp_proto);
1022     if (vlc_proto == NULL)
1023     {
1024         msg_Dbg (p_obj, "unknown SDP media protocol: %s",
1025                  p_sdp->mediav[0].fmt);
1026         return VLC_EGENERIC;
1027     }
1028 
1029     if (!strcmp (vlc_proto, "udp") || FindAttribute (p_sdp, 0, "rtcp-mux"))
1030         p_sdp->rtcp_port = 0;
1031     else
1032     {
1033         const char *rtcp = FindAttribute (p_sdp, 0, "rtcp");
1034         if (rtcp)
1035             p_sdp->rtcp_port = atoi (rtcp);
1036         else
1037         if (port & 1) /* odd port -> RTCP; next even port -> RTP */
1038             p_sdp->rtcp_port = port++;
1039         else /* even port -> RTP; next odd port -> RTCP */
1040             p_sdp->rtcp_port = port + 1;
1041     }
1042 
1043     if (flags & 1)
1044     {
1045         /* Connection-oriented media */
1046         const char *setup = FindAttribute (p_sdp, 0, "setup");
1047         if (setup == NULL)
1048             setup = "active"; /* default value */
1049 
1050         if (strcmp (setup, "actpass") && strcmp (setup, "passive"))
1051         {
1052             msg_Dbg (p_obj, "unsupported COMEDIA mode: %s", setup);
1053             return VLC_EGENERIC;
1054         }
1055 
1056         if (asprintf (&p_sdp->psz_uri, "%s://%s:%d", vlc_proto,
1057                       host, port) == -1)
1058             return VLC_ENOMEM;
1059     }
1060     else
1061     {
1062         /* Non-connected (normally multicast) media */
1063         char psz_source[258] = "";
1064         const char *sfilter = FindAttribute (p_sdp, 0, "source-filter");
1065         if (sfilter != NULL)
1066         {
1067             char psz_source_ip[256];
1068             unsigned ipv;
1069 
1070             if (sscanf (sfilter, " incl IN IP%u %*s %255s ", &ipv,
1071                         psz_source_ip) == 2)
1072             {
1073                 /* According to RFC4570, FQDNs can be used for source-filters,
1074                  * but -seriously- this is impractical */
1075                 switch (ipv)
1076                 {
1077 #ifdef AF_INET6
1078                     case 6:
1079                     {
1080                         struct in6_addr addr;
1081                         if ((inet_pton (AF_INET6, psz_source_ip, &addr) > 0)
1082                         && (inet_ntop (AF_INET6, &addr, psz_source + 1,
1083                                         sizeof (psz_source) - 2) != NULL))
1084                         {
1085                             psz_source[0] = '[';
1086                             psz_source[strlen (psz_source)] = ']';
1087                         }
1088                         break;
1089                     }
1090 #endif
1091                     case 4:
1092                     {
1093                         struct in_addr addr;
1094                         if ((inet_pton (AF_INET, psz_source_ip, &addr) > 0)
1095                         && (inet_ntop (AF_INET, &addr, psz_source,
1096                                         sizeof (psz_source)) == NULL))
1097                             *psz_source = '\0';
1098                         break;
1099                     }
1100                 }
1101             }
1102         }
1103 
1104         if (asprintf (&p_sdp->psz_uri, "%s://%s@%s:%i", vlc_proto, psz_source,
1105                      host, port) == -1)
1106             return VLC_ENOMEM;
1107     }
1108 
1109     return VLC_SUCCESS;
1110 }
1111 
1112 
ParseSDPConnection(const char * str,struct sockaddr_storage * addr,socklen_t * addrlen,unsigned * number)1113 static int ParseSDPConnection (const char *str, struct sockaddr_storage *addr,
1114                                socklen_t *addrlen, unsigned *number)
1115 {
1116     char host[60];
1117     unsigned fam, n1, n2;
1118 
1119     int res = sscanf (str, "IN IP%u %59[^/]/%u/%u", &fam, host, &n1, &n2);
1120     if (res < 2)
1121         return -1;
1122 
1123     switch (fam)
1124     {
1125 #ifdef AF_INET6
1126         case 6:
1127             addr->ss_family = AF_INET6;
1128 # ifdef HAVE_SA_LEN
1129             addr->ss_len =
1130 # endif
1131            *addrlen = sizeof (struct sockaddr_in6);
1132 
1133             if (inet_pton (AF_INET6, host,
1134                            &((struct sockaddr_in6 *)addr)->sin6_addr) <= 0)
1135                 return -1;
1136 
1137             *number = (res >= 3) ? n1 : 1;
1138             break;
1139 #endif
1140 
1141         case 4:
1142             addr->ss_family = AF_INET;
1143 # ifdef HAVE_SA_LEN
1144             addr->ss_len =
1145 # endif
1146            *addrlen = sizeof (struct sockaddr_in);
1147 
1148             if (inet_pton (AF_INET, host,
1149                            &((struct sockaddr_in *)addr)->sin_addr) <= 0)
1150                 return -1;
1151 
1152             *number = (res >= 4) ? n2 : 1;
1153             break;
1154 
1155         default:
1156             return -1;
1157     }
1158     return 0;
1159 }
1160 
1161 
1162 /***********************************************************************
1163  * ParseSDP : SDP parsing
1164  * *********************************************************************
1165  * Validate SDP and parse all fields
1166  ***********************************************************************/
ParseSDP(vlc_object_t * p_obj,const char * psz_sdp)1167 static sdp_t *ParseSDP (vlc_object_t *p_obj, const char *psz_sdp)
1168 {
1169     if( psz_sdp == NULL )
1170         return NULL;
1171 
1172     sdp_t *p_sdp = calloc (1, sizeof (*p_sdp));
1173     if (p_sdp == NULL)
1174         return NULL;
1175 
1176     char expect = 'V';
1177     struct sockaddr_storage glob_addr;
1178     memset (&glob_addr, 0, sizeof (glob_addr));
1179     socklen_t glob_len = 0;
1180     unsigned glob_count = 1;
1181     int port = 0;
1182 
1183     /* TODO: use iconv and charset attribute instead of EnsureUTF8 */
1184     while (*psz_sdp)
1185     {
1186         /* Extract one line */
1187         size_t linelen = strcspn(psz_sdp, "\n");
1188         if (psz_sdp[linelen] == '\0')
1189             goto error;
1190 
1191         char line[linelen + 1];
1192         memcpy (line, psz_sdp, linelen);
1193         line[linelen] = '\0';
1194 
1195         psz_sdp += linelen + 1;
1196 
1197         /* Remove carriage return if present */
1198         char *eol = strchr (line, '\r');
1199         if (eol != NULL)
1200         {
1201             linelen = eol - line;
1202             line[linelen] = '\0';
1203         }
1204 
1205         /* Validate line */
1206         char cat = line[0], *data = line + 2;
1207         if (!cat || (strchr ("vosiuepcbtrzkam", cat) == NULL))
1208         {
1209             /* MUST ignore SDP with unknown line type */
1210             msg_Dbg (p_obj, "unknown SDP line type: 0x%02x", (int)cat);
1211             goto error;
1212         }
1213         if (line[1] != '=')
1214         {
1215             msg_Dbg (p_obj, "invalid SDP line: %s", line);
1216             goto error;
1217         }
1218 
1219         assert (linelen >= 2);
1220 
1221         /* SDP parsing state machine
1222          * We INTERNALLY use uppercase for session, lowercase for media
1223          */
1224         switch (expect)
1225         {
1226             /* Session description */
1227             case 'V':
1228                 expect = 'O';
1229                 if (cat != 'v')
1230                 {
1231                     msg_Dbg (p_obj, "missing SDP version");
1232                     goto error;
1233                 }
1234                 if (strcmp (data, "0"))
1235                 {
1236                     msg_Dbg (p_obj, "unknown SDP version: %s", data);
1237                     goto error;
1238                 }
1239                 break;
1240 
1241             case 'O':
1242                 expect = 'S';
1243                 if (cat != 'o')
1244                 {
1245                     msg_Dbg (p_obj, "missing SDP originator");
1246                     goto error;
1247                 }
1248 
1249                 if ((sscanf (data, "%63s %"SCNu64" %"SCNu64" IN IP%u %1023s",
1250                              p_sdp->username, &p_sdp->session_id,
1251                              &p_sdp->session_version, &p_sdp->orig_ip_version,
1252                              p_sdp->orig_host) != 5)
1253                  || ((p_sdp->orig_ip_version != 4)
1254                   && (p_sdp->orig_ip_version != 6)))
1255                 {
1256                     msg_Dbg (p_obj, "SDP origin not supported: %s", data);
1257                     /* Or maybe out-of-range, but this looks suspicious */
1258                     goto error;
1259                 }
1260                 EnsureUTF8 (p_sdp->orig_host);
1261                 break;
1262 
1263             case 'S':
1264                 expect = 'I';
1265                 if ((cat != 's') || !*data)
1266                 {
1267                     /* MUST be present AND non-empty */
1268                     msg_Dbg (p_obj, "missing SDP session name");
1269                     goto error;
1270                 }
1271                 assert (p_sdp->psz_sessionname == NULL); // no memleak here
1272                 p_sdp->psz_sessionname = strdup (data);
1273                 if (p_sdp->psz_sessionname == NULL)
1274                     goto error;
1275                 EnsureUTF8 (p_sdp->psz_sessionname);
1276                 break;
1277 
1278             case 'I':
1279                 expect = 'U';
1280                 /* optional (and may be empty) */
1281                 if (cat == 'i')
1282                 {
1283                     assert (p_sdp->psz_sessioninfo == NULL);
1284                     p_sdp->psz_sessioninfo = strdup (data);
1285                     if (p_sdp->psz_sessioninfo == NULL)
1286                         goto error;
1287                     EnsureUTF8 (p_sdp->psz_sessioninfo);
1288                     break;
1289                 }
1290                 /* fall through */
1291 
1292             case 'U':
1293                 expect = 'E';
1294                 if (cat == 'u')
1295                     break;
1296                 /* fall through */
1297             case 'E':
1298                 expect = 'E';
1299                 if (cat == 'e')
1300                     break;
1301                 /* fall through */
1302             case 'P':
1303                 expect = 'P';
1304                 if (cat == 'p')
1305                     break;
1306                 /* fall through */
1307             case 'C':
1308                 expect = 'B';
1309                 if (cat == 'c')
1310                 {
1311                     if (ParseSDPConnection (data, &glob_addr, &glob_len,
1312                                             &glob_count))
1313                     {
1314                         msg_Dbg (p_obj, "SDP connection infos not supported: "
1315                                  "%s", data);
1316                         goto error;
1317                     }
1318                     break;
1319                 }
1320                 /* fall through */
1321             case 'B':
1322                 assert (expect == 'B');
1323                 if (cat == 'b')
1324                     break;
1325                 /* fall through */
1326             case 'T':
1327                 expect = 'R';
1328                 if (cat != 't')
1329                 {
1330                     msg_Dbg (p_obj, "missing SDP time description");
1331                     goto error;
1332                 }
1333                 break;
1334 
1335             case 'R':
1336                 if ((cat == 't') || (cat == 'r'))
1337                     break;
1338                 /* fall through */
1339             case 'Z':
1340                 expect = 'K';
1341                 if (cat == 'z')
1342                     break;
1343                 /* fall through */
1344             case 'K':
1345                 expect = 'A';
1346                 if (cat == 'k')
1347                     break;
1348                 /* fall through */
1349             case 'A':
1350                 //expect = 'A';
1351                 if (cat == 'a')
1352                 {
1353                     attribute_t *p_attr = MakeAttribute (data);
1354                     TAB_APPEND( p_sdp->i_attributes, p_sdp->pp_attributes, p_attr );
1355                     break;
1356                 }
1357                 /* fall through */
1358 
1359             /* Media description */
1360             case 'm':
1361             media:
1362             {
1363                 expect = 'i';
1364                 if (cat != 'm')
1365                 {
1366                     msg_Dbg (p_obj, "missing SDP media description");
1367                     goto error;
1368                 }
1369                 struct sdp_media_t *m;
1370                 m = realloc (p_sdp->mediav, (p_sdp->mediac + 1) * sizeof (*m));
1371                 if (m == NULL)
1372                     goto error;
1373 
1374                 p_sdp->mediav = m;
1375                 m += p_sdp->mediac;
1376                 p_sdp->mediac++;
1377 
1378                 memset (m, 0, sizeof (*m));
1379                 memcpy (&m->addr, &glob_addr, m->addrlen = glob_len);
1380                 m->n_addr = glob_count;
1381 
1382                 /* TODO: remember media type (if we need multiple medias) */
1383                 data = strchr (data, ' ');
1384                 if (data == NULL)
1385                 {
1386                     msg_Dbg (p_obj, "missing SDP media port");
1387                     goto error;
1388                 }
1389                 port = atoi (++data);
1390                 if (port <= 0 || port >= 65536)
1391                 {
1392                     msg_Dbg (p_obj, "invalid transport port %d", port);
1393                     goto error;
1394                 }
1395                 net_SetPort ((struct sockaddr *)&m->addr, htons (port));
1396 
1397                 data = strchr (data, ' ');
1398                 if (data == NULL)
1399                 {
1400                     msg_Dbg (p_obj, "missing SDP media format");
1401                     goto error;
1402                 }
1403                 m->fmt = strdup (++data);
1404                 if (m->fmt == NULL)
1405                     goto error;
1406 
1407                 break;
1408             }
1409 
1410             case 'i':
1411                 expect = 'c';
1412                 if (cat == 'i')
1413                     break;
1414                 /* fall through */
1415             case 'c':
1416                 expect = 'b';
1417                 if (cat == 'c')
1418                 {
1419                     struct sdp_media_t *m = p_sdp->mediav + p_sdp->mediac - 1;
1420                     if (ParseSDPConnection (data, &m->addr, &m->addrlen,
1421                                             &m->n_addr))
1422                     {
1423                         msg_Dbg (p_obj, "SDP connection infos not supported: "
1424                                  "%s", data);
1425                         goto error;
1426                     }
1427                     net_SetPort ((struct sockaddr *)&m->addr, htons (port));
1428                     break;
1429                 }
1430                 /* fall through */
1431             case 'b':
1432                 expect = 'b';
1433                 if (cat == 'b')
1434                     break;
1435                 /* fall through */
1436             case 'k':
1437                 expect = 'a';
1438                 if (cat == 'k')
1439                     break;
1440                 /* fall through */
1441             case 'a':
1442                 assert (expect == 'a');
1443                 if (cat == 'a')
1444                 {
1445                     attribute_t *p_attr = MakeAttribute (data);
1446                     if (p_attr == NULL)
1447                         goto error;
1448 
1449                     TAB_APPEND (p_sdp->mediav[p_sdp->mediac - 1].i_attributes,
1450                                 p_sdp->mediav[p_sdp->mediac - 1].pp_attributes, p_attr);
1451                     break;
1452                 }
1453 
1454                 if (cat == 'm')
1455                     goto media;
1456 
1457                 msg_Dbg (p_obj, "unexpected SDP line: 0x%02x", (int)cat);
1458                 goto error;
1459 
1460             default:
1461                 msg_Err (p_obj, "*** BUG in SDP parser! ***");
1462                 goto error;
1463         }
1464     }
1465 
1466     return p_sdp;
1467 
1468 error:
1469     FreeSDP (p_sdp);
1470     return NULL;
1471 }
1472 
InitSocket(services_discovery_t * p_sd,const char * psz_address,int i_port)1473 static int InitSocket( services_discovery_t *p_sd, const char *psz_address,
1474                        int i_port )
1475 {
1476     int i_fd = net_ListenUDP1 ((vlc_object_t *)p_sd, psz_address, i_port);
1477     if (i_fd == -1)
1478         return VLC_EGENERIC;
1479 
1480     shutdown( i_fd, SHUT_WR );
1481     TAB_APPEND(p_sd->p_sys->i_fd, p_sd->p_sys->pi_fd, i_fd);
1482     return VLC_SUCCESS;
1483 }
1484 
Decompress(const unsigned char * psz_src,unsigned char ** _dst,int i_len)1485 static int Decompress( const unsigned char *psz_src, unsigned char **_dst, int i_len )
1486 {
1487 #ifdef HAVE_ZLIB_H
1488     int i_result, i_dstsize, n = 0;
1489     unsigned char *psz_dst = NULL;
1490     z_stream d_stream;
1491 
1492     memset (&d_stream, 0, sizeof (d_stream));
1493 
1494     i_result = inflateInit(&d_stream);
1495     if( i_result != Z_OK )
1496         return( -1 );
1497 
1498     d_stream.next_in = (Bytef *)psz_src;
1499     d_stream.avail_in = i_len;
1500 
1501     do
1502     {
1503         n++;
1504         psz_dst = xrealloc( psz_dst, n * 1000 );
1505         d_stream.next_out = (Bytef *)&psz_dst[(n - 1) * 1000];
1506         d_stream.avail_out = 1000;
1507 
1508         i_result = inflate(&d_stream, Z_NO_FLUSH);
1509         if( ( i_result != Z_OK ) && ( i_result != Z_STREAM_END ) )
1510         {
1511             inflateEnd( &d_stream );
1512             free( psz_dst );
1513             return( -1 );
1514         }
1515     }
1516     while( ( d_stream.avail_out == 0 ) && ( d_stream.avail_in != 0 ) &&
1517            ( i_result != Z_STREAM_END ) );
1518 
1519     i_dstsize = d_stream.total_out;
1520     inflateEnd( &d_stream );
1521 
1522     *_dst = xrealloc( psz_dst, i_dstsize );
1523 
1524     return i_dstsize;
1525 #else
1526     (void)psz_src;
1527     (void)_dst;
1528     (void)i_len;
1529     return -1;
1530 #endif
1531 }
1532 
1533 
FreeSDP(sdp_t * p_sdp)1534 static void FreeSDP( sdp_t *p_sdp )
1535 {
1536     free( p_sdp->psz_sessionname );
1537     free( p_sdp->psz_sessioninfo );
1538     free( p_sdp->psz_uri );
1539 
1540     for (unsigned j = 0; j < p_sdp->mediac; j++)
1541     {
1542         free (p_sdp->mediav[j].fmt);
1543         for (int i = 0; i < p_sdp->mediav[j].i_attributes; i++)
1544             FreeAttribute (p_sdp->mediav[j].pp_attributes[i]);
1545         free (p_sdp->mediav[j].pp_attributes);
1546     }
1547     free (p_sdp->mediav);
1548 
1549     for (int i = 0; i < p_sdp->i_attributes; i++)
1550         FreeAttribute (p_sdp->pp_attributes[i]);
1551 
1552     free (p_sdp->pp_attributes);
1553     free (p_sdp);
1554 }
1555 
RemoveAnnounce(services_discovery_t * p_sd,sap_announce_t * p_announce)1556 static int RemoveAnnounce( services_discovery_t *p_sd,
1557                            sap_announce_t *p_announce )
1558 {
1559     if( p_announce->p_sdp )
1560     {
1561         FreeSDP( p_announce->p_sdp );
1562         p_announce->p_sdp = NULL;
1563     }
1564 
1565     if( p_announce->p_item )
1566     {
1567         services_discovery_RemoveItem( p_sd, p_announce->p_item );
1568         input_item_Release( p_announce->p_item );
1569         p_announce->p_item = NULL;
1570     }
1571 
1572     TAB_REMOVE(p_sd->p_sys->i_announces, p_sd->p_sys->pp_announces,
1573                p_announce);
1574     free( p_announce );
1575 
1576     return VLC_SUCCESS;
1577 }
1578 
1579 /*
1580  * Compare two sessions, when hash is not set (SAP v0)
1581  */
IsSameSession(sdp_t * p_sdp1,sdp_t * p_sdp2)1582 static bool IsSameSession( sdp_t *p_sdp1, sdp_t *p_sdp2 )
1583 {
1584     /* A session is identified by
1585      * - username,
1586      * - session_id,
1587      * - network type (which is always IN),
1588      * - address type (currently, this means IP version),
1589      * - and hostname.
1590      */
1591     if (strcmp (p_sdp1->username, p_sdp2->username)
1592      || (p_sdp1->session_id != p_sdp2->session_id)
1593      || (p_sdp1->orig_ip_version != p_sdp2->orig_ip_version)
1594      || strcmp (p_sdp1->orig_host, p_sdp2->orig_host))
1595         return false;
1596 
1597     return true;
1598 }
1599 
MakeAttribute(const char * str)1600 static inline attribute_t *MakeAttribute (const char *str)
1601 {
1602     attribute_t *a = malloc (sizeof (*a) + strlen (str) + 1);
1603     if (a == NULL)
1604         return NULL;
1605 
1606     strcpy (a->name, str);
1607     EnsureUTF8 (a->name);
1608     char *value = strchr (a->name, ':');
1609     if (value != NULL)
1610     {
1611         *value++ = '\0';
1612         a->value = value;
1613     }
1614     else
1615         a->value = "";
1616     return a;
1617 }
1618 
1619 
GetAttribute(attribute_t ** tab,unsigned n,const char * name)1620 static const char *GetAttribute (attribute_t **tab, unsigned n,
1621                                  const char *name)
1622 {
1623     for (unsigned i = 0; i < n; i++)
1624         if (strcasecmp (tab[i]->name, name) == 0)
1625             return tab[i]->value;
1626     return NULL;
1627 }
1628 
1629 
FreeAttribute(attribute_t * a)1630 static inline void FreeAttribute (attribute_t *a)
1631 {
1632     free (a);
1633 }
1634