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