1 /*****************************************************************************
2 * sap.c : SAP interface module
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: sap.c,v 1.28 2003/10/29 17:32:55 zorglub Exp $
7 * Authors: Arnaud Schauly <gitan@via.ecp.fr>
8 * Clément Stenac <zorglub@via.ecp.fr>
9 * Damien Lucas <nitrox@videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
29 #include <stdlib.h> /* malloc(), free() */
32 #include <errno.h> /* ENOMEM */
45 #ifdef HAVE_SYS_TIME_H
46 # include <sys/time.h>
48 #include <sys/types.h>
52 # include <winsock2.h>
53 # include <ws2tcpip.h>
55 # define IN_MULTICAST(a) IN_CLASSD(a)
58 # include <sys/socket.h>
59 # include <netinet/in.h>
61 # include <arpa/inet.h>
62 # elif defined( SYS_BEOS )
63 # include <net/netdb.h>
68 # define close(a) CloseHandle(a)
69 #elif defined( WIN32 )
70 # define close(a) closesocket(a)
75 #define MAX_LINE_LENGTH 256
77 /* SAP is always on that port */
78 #define HELLO_PORT 9875
79 #define HELLO_GROUP "224.2.127.254"
82 #define IPV6_ADDR_1 "FF0" /* Scope is inserted between them */
83 #define IPV6_ADDR_2 "::2:7FFE"
85 /*****************************************************************************
87 *****************************************************************************/
89 typedef struct media_descr_t media_descr_t;
90 typedef struct sess_descr_t sess_descr_t;
91 typedef struct attr_descr_t attr_descr_t;
93 static int Activate ( vlc_object_t * );
94 static void Run ( intf_thread_t *p_intf );
95 static int Kill ( intf_thread_t * );
97 static ssize_t NetRead ( intf_thread_t*, int, int , byte_t *, size_t );
99 /* playlist related functions */
100 static int sess_toitem( intf_thread_t *, sess_descr_t * );
102 /* sap/sdp related functions */
103 static int parse_sap ( char * );
104 static int packet_handle ( intf_thread_t *, char *, int );
105 static sess_descr_t * parse_sdp( intf_thread_t *, char * ) ;
107 /* specific sdp fields parsing */
109 static void cfield_parse( char *, char ** );
110 static void mfield_parse( char *psz_mfield, char **ppsz_proto,
113 static void free_sd( sess_descr_t * );
115 /* Detect multicast addresses */
116 static int ismult( char * );
118 /* Our custom structure */
124 /* The struct that contains sdp informations */
129 char *psz_sessionname;
130 char *psz_information;
137 char *psz_connection;
140 media_descr_t ** pp_media;
141 attr_descr_t ** pp_attributes;
144 /* All this informations are not useful yet. */
148 char *psz_mediaconnection;
157 /*****************************************************************************
159 *****************************************************************************/
161 #define SAP_ADDR_TEXT N_("SAP multicast address")
162 #define SAP_ADDR_LONGTEXT N_("SAP multicast address")
163 #define SAP_IPV4_TEXT N_("IPv4-SAP listening")
164 #define SAP_IPV4_LONGTEXT N_("Set this if you want SAP to listen for IPv4 announces")
165 #define SAP_IPV6_TEXT N_("IPv6-SAP listening")
166 #define SAP_IPV6_LONGTEXT N_("Set this if you want SAP to listen for IPv6 announces")
167 #define SAP_SCOPE_TEXT N_("IPv6 SAP scope")
168 #define SAP_SCOPE_LONGTEXT N_("Sets the scope for IPv6 announces (default is 8)")
171 add_category_hint( N_("SAP"), NULL, VLC_TRUE );
172 add_string( "sap-addr", NULL, NULL,
173 SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, VLC_TRUE );
175 add_bool( "sap-ipv4", 1 , NULL,
176 SAP_IPV4_TEXT,SAP_IPV4_LONGTEXT, VLC_TRUE);
178 add_bool( "sap-ipv6", 0 , NULL,
179 SAP_IPV6_TEXT, SAP_IPV6_LONGTEXT, VLC_TRUE);
181 add_string( "sap-ipv6-scope", "8" , NULL,
182 SAP_SCOPE_TEXT, SAP_SCOPE_LONGTEXT, VLC_TRUE);
184 set_description( _("SAP interface") );
185 set_capability( "interface", 0 );
186 set_callbacks( Activate, NULL);
189 /*****************************************************************************
190 * Activate: initialize and create stuff
191 *****************************************************************************/
192 static int Activate( vlc_object_t *p_this )
194 intf_thread_t *p_intf = (intf_thread_t*)p_this;
196 p_intf->pf_run = Run;
201 /*****************************************************************************
203 *****************************************************************************
204 * Listens to SAP packets, and sends them to packet_handle
205 *****************************************************************************/
206 #define MAX_SAP_BUFFER 2000
208 static void Run( intf_thread_t *p_intf )
212 char *psz_network = NULL;
216 int sap_ipv4 = config_GetInt( p_intf, "sap-ipv4" );
217 int sap_ipv6 = config_GetInt( p_intf, "sap-ipv6" );
218 char *sap_ipv6_scope = config_GetPsz( p_intf, "sap-ipv6-scope" );
220 char buffer[MAX_SAP_BUFFER + 1];
223 network_socket_t socket_desc;
224 playlist_t *p_playlist;
225 playlist_group_t *p_group;
227 if( sap_ipv4 == -1 || sap_ipv6 == -1 || sap_ipv6_scope == NULL )
229 msg_Warn( p_intf, "Unable to parse module configuration" );
234 p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
237 msg_Err( p_intf, "Out of memory !");
240 /* Create our playlist group */
242 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
244 p_group = playlist_CreateGroup( p_playlist , "SAP" );
245 p_intf->p_sys->i_group = p_group->i_id;
247 vlc_object_release( p_playlist );
249 /* Prepare IPv4 Networking */
252 if( !(psz_addr = config_GetPsz( p_intf, "sap-addr" ) ) )
254 psz_addr = strdup( HELLO_GROUP );
257 /* Prepare the network_socket_t structure */
258 socket_desc.i_type = NETWORK_UDP;
259 socket_desc.psz_bind_addr = psz_addr;
260 socket_desc.i_bind_port = HELLO_PORT;
261 socket_desc.psz_server_addr = "";
262 socket_desc.i_server_port = 0;
263 socket_desc.i_ttl = 0;
264 p_intf->p_private = (void*) &socket_desc;
266 psz_network = "ipv4";
268 /* Create, Bind the socket, ... with the appropriate module */
270 if( !( p_network = module_Need( p_intf, "network", psz_network ) ) )
272 msg_Warn( p_intf, "failed to open a connection (udp)" );
275 module_Unneed( p_intf, p_network );
277 fd = socket_desc.i_handle;
280 /* Prepare IPv6 Networking */
283 /* Prepare the network_socket_t structure */
285 psz_addrv6=(char *)malloc(sizeof(char)*38);
289 msg_Warn( p_intf, "Out of memory !" );
291 /* Max size of an IPv6 address */
293 sprintf(psz_addrv6,"[%s%c%s]",IPV6_ADDR_1,
294 sap_ipv6_scope[0],IPV6_ADDR_2);
296 socket_desc.i_type = NETWORK_UDP;
297 socket_desc.psz_bind_addr = psz_addrv6;
298 socket_desc.i_bind_port = HELLO_PORT;
299 socket_desc.psz_server_addr = "";
300 socket_desc.i_server_port = 0;
301 socket_desc.i_ttl = 0;
302 p_intf->p_private = (void*) &socket_desc;
304 psz_network = "ipv6";
306 /* Create, Bind the socket, ... with the appropriate module */
308 if( !( p_network = module_Need( p_intf, "network", psz_network ) ) )
310 msg_Warn( p_intf, "failed to open a connection (udp)" );
313 module_Unneed( p_intf, p_network );
315 fdv6 = socket_desc.i_handle;
319 /* read SAP packets */
320 while( !p_intf->b_die )
324 //memset( buffer, 0, MAX_SAP_BUFFER + 1);
326 i_read = NetRead( p_intf, fd, fdv6, buffer, MAX_SAP_BUFFER );
330 msg_Err( p_intf, "Cannot read in the socket" );
336 buffer[i_read] = '\0';
338 packet_handle( p_intf, buffer, i_read );
347 msg_Warn( p_intf, "Ohoh, unable to close the socket" );
354 msg_Warn( p_intf, "Ohoh, unable to close the socket" );
359 /********************************************************************
361 *******************************************************************
362 * Kills the SAP interface.
363 ********************************************************************/
364 static int Kill( intf_thread_t *p_intf )
367 p_intf->b_die = VLC_TRUE;
372 /*******************************************************************
373 * sess_toitem : changes a sess_descr_t into a hurd of
374 * playlist_item_t, which are enqueued.
375 *******************************************************************
376 * Note : does not support sessions that take place on consecutive
377 * port or adresses yet.
378 *******************************************************************/
380 static int sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
382 playlist_item_t * p_item;
383 char *psz_uri, *psz_proto;
385 char *psz_uri_default;
388 vlc_bool_t b_http = VLC_FALSE;
389 char *psz_http_path = NULL;
390 playlist_t *p_playlist;
392 psz_uri_default = NULL;
393 cfield_parse( p_sd->psz_connection, &psz_uri_default );
395 for( i_count = 0 ; i_count < p_sd->i_media ; i_count++ )
397 p_item = malloc( sizeof( playlist_item_t ) );
400 msg_Err( p_intf, "Not enough memory for p_item in sesstoitem()" );
403 p_item->psz_name = strdup( p_sd->psz_sessionname );
405 p_item->i_status = 0;
406 p_item->b_autodeletion = VLC_FALSE;
407 p_item->psz_uri = NULL;
408 p_item->ppsz_options = NULL;
409 p_item->i_options = 0;
413 /* Build what we have to put in p_item->psz_uri, with the m and
416 if( !p_sd->pp_media[i_count] )
421 mfield_parse( p_sd->pp_media[i_count]->psz_medianame,
422 & psz_proto, & psz_port );
424 if( !psz_proto || !psz_port )
429 if( p_sd->pp_media[i_count]->psz_mediaconnection )
431 cfield_parse( p_sd->pp_media[i_count]->psz_mediaconnection,
436 psz_uri = psz_uri_default;
439 if( psz_uri == NULL )
444 for( i = 0 ; i< p_sd->i_attributes ; i++ )
446 if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "type") &&
447 strstr( p_sd->pp_attributes[i]->psz_value, "http") )
451 if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "http-path"))
453 psz_http_path = strdup( p_sd->pp_attributes[i]->psz_value );
459 /* Filling p_item->psz_uri */
460 if( b_http == VLC_FALSE )
462 i_multicast = ismult( psz_uri );
464 p_item->psz_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
465 strlen( psz_port ) + 5 +i_multicast );
467 if( p_item->psz_uri == NULL )
469 msg_Err( p_intf, "Not enough memory");
474 if( i_multicast == 1)
476 sprintf( p_item->psz_uri, "%s://@%s:%s", psz_proto,
481 sprintf( p_item->psz_uri, "%s://%s:%s", psz_proto,
487 if( psz_http_path == NULL )
488 psz_http_path = strdup("/");
490 p_item->psz_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
491 strlen( psz_port ) + 3 + strlen(psz_http_path) );
493 if( p_item->psz_uri == NULL )
495 msg_Err( p_intf, "Not enough memory");
500 sprintf( p_item->psz_uri, "%s://%s:%s%s", psz_proto,
501 psz_uri, psz_port,psz_http_path );
504 /* Enqueueing p_item in the playlist */
508 p_item->i_group = p_intf->p_sys->i_group;
509 p_item->b_enabled = VLC_TRUE;
510 p_item->psz_author = NULL;
511 p_playlist = vlc_object_find( p_intf,
512 VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
514 playlist_AddItem ( p_playlist, p_item,
515 PLAYLIST_CHECK_INSERT, PLAYLIST_END);
516 vlc_object_release( p_playlist );
527 /**********************************************************************
529 *********************************************************************
530 * put into *ppsz_uri, the the uri in the cfield, psz_cfield.
531 *********************************************************************/
533 static void cfield_parse( char *psz_cfield, char **ppsz_uri )
539 psz_pos = psz_cfield;
541 while( *psz_pos != ' ' && *psz_pos !='\0' )
546 while( *psz_pos != ' ' && *psz_pos !='\0' )
552 while( *psz_pos != ' ' && *psz_pos !='/'
553 && *psz_pos != '\0' )
569 /**********************************************************************
571 *********************************************************************
572 * put into *ppsz_proto, and *ppsz_port, the protocol and the port.
573 *********************************************************************/
576 static void mfield_parse( char *psz_mfield, char **ppsz_proto,
582 psz_pos = psz_mfield;
583 while( *psz_pos != '\0' && *psz_pos != ' ' )
588 *ppsz_port = psz_pos;
589 while( *psz_pos != '\0' && *psz_pos && *psz_pos !=' ' && *psz_pos!='/' )
593 if( *psz_pos == '/' ) // FIXME does not support multi-port
597 while( *psz_pos != '\0' && *psz_pos !=' ' )
604 *ppsz_proto = psz_pos;
605 while( *psz_pos!='\0' && *psz_pos !=' ' &&
608 *psz_pos = tolower( *psz_pos );
621 /***********************************************************************
622 * parse_sap : Takes care of the SAP headers
623 ***********************************************************************
624 * checks if the packet has the true headers ;
625 * returns the SAP header lenhth
626 ***********************************************************************/
628 static int parse_sap( char *p_packet )
630 // According to RFC 2974
631 int i_hlen = 4; // Minimum header length is 4
632 i_hlen += (p_packet[0] & 0x10) ? 16 : 4; // Address type IPv6=16bytes
633 i_hlen += p_packet[1]; // Authentification length
635 //Looks for the first '\0' byte after length
636 for(;p_packet[i_hlen]!='\0'; i_hlen++);
638 if( i_hlen > 50 ) /* Definitely too long...
639 Maybe we have a fucked up packet without \0 */
640 { /* As a workaround, we search for "v=" */
642 for(;p_packet[i_hlen] != 'v' && p_packet[i_hlen+1] != '=' ; i_hlen++);
649 /*************************************************************************
650 * packet_handle : handle the received packet and enques the
651 * the understated session
652 *************************************************************************/
654 static int packet_handle( intf_thread_t * p_intf, char *p_packet, int i_len )
657 int i_hlen; // Header length
659 i_hlen = parse_sap(p_packet);
661 if( (i_hlen > 0) && (i_hlen < i_len) )
663 p_sd = parse_sdp( p_intf, p_packet + i_hlen +1);
666 sess_toitem ( p_intf, p_sd );
672 return VLC_FALSE; // Invalid Packet
678 /***********************************************************************
679 * parse_sdp : SDP parsing
680 * *********************************************************************
681 * Make a sess_descr_t with a psz
682 ***********************************************************************/
684 static sess_descr_t * parse_sdp( intf_thread_t * p_intf, char *p_packet )
690 // According to RFC 2327, the first bytes should be exactly "v="
691 if((p_packet[0] != 'v') || (p_packet[1] != '='))
693 msg_Warn(p_intf, "Bad SDP packet");
697 if( ( sd = malloc( sizeof(sess_descr_t) ) ) == NULL )
699 msg_Err( p_intf, "Not enough memory for sd in parse_sdp()" );
704 sd->pp_attributes = NULL;
705 sd->psz_origin = NULL;
706 sd->psz_sessionname = NULL;
707 sd->psz_information = NULL;
709 sd->psz_emails = NULL;
710 sd->psz_phone = NULL;
712 sd->psz_repeat = NULL;
713 sd->psz_attribute = NULL;
714 sd->psz_connection = NULL;
718 sd->i_attributes = 0;
720 while( *p_packet != '\0' )
722 #define FIELD_COPY( p ) \
723 p = strndup( &p_packet[2], i_field_len );
728 while( *p_packet == '\n' || *p_packet == ' ' || *p_packet == '\t' )
732 if( *p_packet == '\0' )
737 if( ( psz_end = strchr( p_packet, '\n' ) ) == NULL )
739 psz_end = p_packet + strlen( p_packet );
741 i_field_len = psz_end - &p_packet[2];
743 if( p_packet[1] == '=' && i_field_len > 0)
748 FIELD_COPY( sd->psz_version );
751 FIELD_COPY( sd->psz_origin );
754 FIELD_COPY( sd->psz_sessionname );
757 FIELD_COPY( sd->psz_information );
760 FIELD_COPY( sd->psz_uri );
763 FIELD_COPY( sd->psz_emails );
766 FIELD_COPY( sd->psz_phone );
769 FIELD_COPY( sd->psz_time );
772 FIELD_COPY( sd->psz_repeat );
775 if( sd->pp_attributes )
778 realloc( sd->pp_attributes,
779 sizeof( attr_descr_t ) *
780 ( sd->i_attributes +1 ) );
784 sd->pp_attributes = malloc( sizeof( void * ) );
786 if( !sd->pp_attributes )
788 msg_Warn( p_intf, "Out of memory !" );
791 sd->pp_attributes[sd->i_attributes] =
792 malloc( sizeof( attr_descr_t ) );
793 if( ! sd->pp_attributes[sd->i_attributes])
795 msg_Warn( p_intf, "Out of memory !" );
800 psz_eof = strchr( p_packet, ':');
803 sd->pp_attributes[sd->i_attributes]->psz_field =
807 sd->pp_attributes[sd->i_attributes]->psz_value =
812 if( sd->pp_attributes[sd->i_attributes]->psz_field )
813 free( sd->pp_attributes[sd->i_attributes]
818 strlen(sd->pp_attributes[sd->i_attributes]->psz_value) ;
821 if(sd->pp_attributes[sd->i_attributes]->psz_value[i]
823 sd->pp_attributes[sd->i_attributes]->psz_value[i] =0;
832 realloc( sd->pp_media,
833 sizeof( media_descr_t ) * ( sd->i_media + 1 ) );
837 sd->pp_media = malloc( sizeof( void * ) );
841 msg_Warn( p_intf, "Out of memory !" );
844 sd->pp_media[sd->i_media] =
845 malloc( sizeof( media_descr_t ) );
846 sd->pp_media[sd->i_media]->psz_medianame = NULL;
847 sd->pp_media[sd->i_media]->psz_mediaconnection = NULL;
848 sd->pp_media[sd->i_media]->psz_medianame =
849 strndup( &p_packet[2], i_field_len );
855 if( sd->i_media <= 0 )
857 FIELD_COPY(sd->psz_connection);
861 FIELD_COPY(sd->pp_media[sd->i_media - 1]->psz_mediaconnection);
876 if( p ) { free( p ); (p) = NULL; }
877 static void free_sd( sess_descr_t * p_sd )
882 FREE( p_sd->psz_origin );
883 FREE( p_sd->psz_sessionname );
884 FREE( p_sd->psz_information );
885 FREE( p_sd->psz_uri );
886 FREE( p_sd->psz_emails );
887 FREE( p_sd->psz_phone );
888 FREE( p_sd->psz_time );
889 FREE( p_sd->psz_repeat );
890 FREE( p_sd->psz_attribute );
891 FREE( p_sd->psz_connection );
893 for( i = 0; i < p_sd->i_media ; i++ )
895 FREE( p_sd->pp_media[i]->psz_medianame );
896 FREE( p_sd->pp_media[i]->psz_mediaconnection );
898 for( i = 0; i < p_sd->i_attributes ; i++ )
900 FREE( p_sd->pp_attributes[i]->psz_field );
901 FREE( p_sd->pp_attributes[i]->psz_value );
903 FREE( p_sd->pp_attributes );
904 FREE( p_sd->pp_media );
915 /***********************************************************************
916 * ismult: returns true if we have a multicast address
917 ***********************************************************************/
919 static int ismult( char *psz_uri )
924 i_value = strtol( psz_uri, &psz_end, 0 );
927 if( psz_uri[0] == '[')
929 if( strncasecmp( &psz_uri[1], "FF0" , 3) ||
930 strncasecmp( &psz_uri[2], "FF0" , 3))
936 if( *psz_end != '.' ) { return( VLC_FALSE ); }
938 return( i_value < 224 ? VLC_FALSE : VLC_TRUE );
943 /*****************************************************************************
944 * Read: read on a file descriptor, checking b_die periodically
945 *****************************************************************************
947 ******************************************************************************/
948 static ssize_t NetRead( intf_thread_t *p_intf,
949 int i_handle, int i_handle_v6,
950 byte_t *p_buffer, size_t i_len)
956 struct timeval timeout;
963 /* Get the max handle for select */
964 if( i_handle_v6 > i_handle )
965 i_max_handle = i_handle_v6;
967 i_max_handle = i_handle;
970 /* Initialize file descriptor set */
972 if( i_handle > 0 ) FD_SET( i_handle, &fds );
973 if( i_handle_v6 > 0 ) FD_SET( i_handle_v6, &fds);
976 /* We'll wait 0.5 second if nothing happens */
978 timeout.tv_usec = 500000;
980 /* Find if some data is available */
981 i_ret = select( i_max_handle + 1, &fds,
982 NULL, NULL, &timeout );
984 if( i_ret == -1 && errno != EINTR )
986 msg_Err( p_intf, "network select error (%s)", strerror(errno) );
993 if(FD_ISSET( i_handle, &fds ))
995 i_recv = recv( i_handle, p_buffer, i_len, 0 );
1000 if(FD_ISSET( i_handle_v6, &fds ))
1002 i_recv = recv( i_handle_v6, p_buffer, i_len, 0 );
1008 msg_Err( p_intf, "recv failed (%s)", strerror(errno) );