1 /*****************************************************************************
2 * sap.c : SAP interface module
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: sap.c,v 1.24 2003/10/06 16:23:30 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 /* The struct that contains sdp informations */
123 char *psz_sessionname;
124 char *psz_information;
131 char *psz_connection;
134 media_descr_t ** pp_media;
135 attr_descr_t ** pp_attributes;
138 /* All this informations are not useful yet. */
142 char *psz_mediaconnection;
151 /*****************************************************************************
153 *****************************************************************************/
155 #define SAP_ADDR_TEXT N_("SAP multicast address")
156 #define SAP_ADDR_LONGTEXT N_("SAP multicast address")
157 #define SAP_IPV4_TEXT N_("No IPv4-SAP listening")
158 #define SAP_IPV4_LONGTEXT N_("Set this if you do not want SAP to listen for IPv4 announces")
159 #define SAP_IPV6_TEXT N_("IPv6-SAP listening")
160 #define SAP_IPV6_LONGTEXT N_("Set this if you want SAP to listen for IPv6 announces")
161 #define SAP_SCOPE_TEXT N_("IPv6 SAP scope")
162 #define SAP_SCOPE_LONGTEXT N_("Sets the scope for IPv6 announces (default is 8)")
163 #define SAP_GROUP_ID_TEXT N_("SAP Playlist group ID")
164 #define SAP_GROUP_ID_LONGTEXT N_("Sets the default group ID in which" \
165 "SAP items are put" )
168 add_category_hint( N_("SAP"), NULL, VLC_TRUE );
169 add_string( "sap-addr", NULL, NULL,
170 SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, VLC_TRUE );
172 add_bool( "no-sap-ipv4", 0 , NULL,
173 SAP_IPV4_TEXT,SAP_IPV4_LONGTEXT, VLC_TRUE);
175 add_bool( "sap-ipv6", 0 , NULL,
176 SAP_IPV6_TEXT, SAP_IPV6_LONGTEXT, VLC_TRUE);
178 add_string( "sap-ipv6-scope", "8" , NULL,
179 SAP_SCOPE_TEXT, SAP_SCOPE_LONGTEXT, VLC_TRUE);
181 add_integer( "sap-group-id", 42, NULL,
182 SAP_GROUP_ID_TEXT, SAP_GROUP_ID_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 no_sap_ipv4 = config_GetInt( p_intf, "no-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;
225 if( no_sap_ipv4 == -1 || sap_ipv6 == -1 || sap_ipv6_scope == NULL )
227 msg_Warn( p_intf, "Unable to parse module configuration" );
231 /* Prepare IPv4 Networking */
232 if ( no_sap_ipv4 == 0)
234 if( !(psz_addr = config_GetPsz( p_intf, "sap-addr" ) ) )
236 psz_addr = strdup( HELLO_GROUP );
239 /* Prepare the network_socket_t structure */
240 socket_desc.i_type = NETWORK_UDP;
241 socket_desc.psz_bind_addr = psz_addr;
242 socket_desc.i_bind_port = HELLO_PORT;
243 socket_desc.psz_server_addr = "";
244 socket_desc.i_server_port = 0;
245 socket_desc.i_ttl = 0;
246 p_intf->p_private = (void*) &socket_desc;
248 psz_network = "ipv4";
250 /* Create, Bind the socket, ... with the appropriate module */
252 if( !( p_network = module_Need( p_intf, "network", psz_network ) ) )
254 msg_Warn( p_intf, "failed to open a connection (udp)" );
257 module_Unneed( p_intf, p_network );
259 fd = socket_desc.i_handle;
262 /* Prepare IPv6 Networking */
265 /* Prepare the network_socket_t structure */
267 psz_addrv6=(char *)malloc(sizeof(char)*38);
268 /* Max size of an IPv6 address */
270 sprintf(psz_addrv6,"[%s%c%s]",IPV6_ADDR_1,
271 sap_ipv6_scope[0],IPV6_ADDR_2);
273 socket_desc.i_type = NETWORK_UDP;
274 socket_desc.psz_bind_addr = psz_addrv6;
275 socket_desc.i_bind_port = HELLO_PORT;
276 socket_desc.psz_server_addr = "";
277 socket_desc.i_server_port = 0;
278 socket_desc.i_ttl = 0;
279 p_intf->p_private = (void*) &socket_desc;
281 psz_network = "ipv6";
283 /* Create, Bind the socket, ... with the appropriate module */
285 if( !( p_network = module_Need( p_intf, "network", psz_network ) ) )
287 msg_Warn( p_intf, "failed to open a connection (udp)" );
290 module_Unneed( p_intf, p_network );
292 fdv6 = socket_desc.i_handle;
296 /* read SAP packets */
297 while( !p_intf->b_die )
301 //memset( buffer, 0, MAX_SAP_BUFFER + 1);
303 i_read = NetRead( p_intf, fd, fdv6, buffer, MAX_SAP_BUFFER );
307 msg_Err( p_intf, "Cannot read in the socket" );
313 buffer[i_read] = '\0';
315 packet_handle( p_intf, buffer, i_read );
320 close( socket_desc.i_handle );
323 /********************************************************************
325 *******************************************************************
326 * Kills the SAP interface.
327 ********************************************************************/
328 static int Kill( intf_thread_t *p_intf )
331 p_intf->b_die = VLC_TRUE;
336 /*******************************************************************
337 * sess_toitem : changes a sess_descr_t into a hurd of
338 * playlist_item_t, which are enqueued.
339 *******************************************************************
340 * Note : does not support sessions that take place on consecutive
341 * port or adresses yet.
342 *******************************************************************/
344 static int sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
346 playlist_item_t * p_item;
347 char *psz_uri, *psz_proto;
349 char *psz_uri_default;
352 vlc_bool_t b_http = VLC_FALSE;
353 char *psz_http_path = NULL;
354 playlist_t *p_playlist;
356 psz_uri_default = NULL;
357 cfield_parse( p_sd->psz_connection, &psz_uri_default );
359 for( i_count = 0 ; i_count < p_sd->i_media ; i_count++ )
361 p_item = malloc( sizeof( playlist_item_t ) );
364 msg_Err( p_intf, "Not enough memory for p_item in sesstoitem()" );
367 p_item->psz_name = strdup( p_sd->psz_sessionname );
369 p_item->i_status = 0;
370 p_item->b_autodeletion = VLC_FALSE;
371 p_item->psz_uri = NULL;
372 p_item->ppsz_options = NULL;
373 p_item->i_options = 0;
377 /* Build what we have to put in p_item->psz_uri, with the m and
380 if( !p_sd->pp_media[i_count] )
385 mfield_parse( p_sd->pp_media[i_count]->psz_medianame,
386 & psz_proto, & psz_port );
388 if( !psz_proto || !psz_port )
393 if( p_sd->pp_media[i_count]->psz_mediaconnection )
395 cfield_parse( p_sd->pp_media[i_count]->psz_mediaconnection,
400 psz_uri = psz_uri_default;
403 if( psz_uri == NULL )
408 for( i = 0 ; i< p_sd->i_attributes ; i++ )
410 if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "type") &&
411 strstr( p_sd->pp_attributes[i]->psz_value, "http") )
415 if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "http-path"))
417 psz_http_path = strdup( p_sd->pp_attributes[i]->psz_value );
423 /* Filling p_item->psz_uri */
424 if( b_http == VLC_FALSE )
426 i_multicast = ismult( psz_uri );
428 p_item->psz_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
429 strlen( psz_port ) + 5 +i_multicast );
431 if( p_item->psz_uri == NULL )
433 msg_Err( p_intf, "Not enough memory");
438 if( i_multicast == 1)
440 sprintf( p_item->psz_uri, "%s://@%s:%s", psz_proto,
445 sprintf( p_item->psz_uri, "%s://%s:%s", psz_proto,
451 if( psz_http_path == NULL )
452 psz_http_path = strdup("/");
454 p_item->psz_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
455 strlen( psz_port ) + 3 + strlen(psz_http_path) );
457 if( p_item->psz_uri == NULL )
459 msg_Err( p_intf, "Not enough memory");
464 sprintf( p_item->psz_uri, "%s://%s:%s%s", psz_proto,
465 psz_uri, psz_port,psz_http_path );
468 /* Enqueueing p_item in the playlist */
472 p_item->i_group = config_GetInt( p_intf, "sap-group-id" );
473 p_item->b_enabled = VLC_TRUE;
474 p_item->psz_author = NULL;
475 p_playlist = vlc_object_find( p_intf,
476 VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
478 playlist_AddItem ( p_playlist, p_item,
479 PLAYLIST_CHECK_INSERT, PLAYLIST_END);
480 vlc_object_release( p_playlist );
491 /**********************************************************************
493 *********************************************************************
494 * put into *ppsz_uri, the the uri in the cfield, psz_cfield.
495 *********************************************************************/
497 static void cfield_parse( char *psz_cfield, char **ppsz_uri )
503 psz_pos = psz_cfield;
505 while( *psz_pos != ' ' && *psz_pos !='\0' )
510 while( *psz_pos != ' ' && *psz_pos !='\0' )
516 while( *psz_pos != ' ' && *psz_pos !='/'
517 && *psz_pos != '\0' )
533 /**********************************************************************
535 *********************************************************************
536 * put into *ppsz_proto, and *ppsz_port, the protocol and the port.
537 *********************************************************************/
540 static void mfield_parse( char *psz_mfield, char **ppsz_proto,
546 psz_pos = psz_mfield;
547 while( *psz_pos != '\0' && *psz_pos != ' ' )
552 *ppsz_port = psz_pos;
553 while( *psz_pos != '\0' && *psz_pos && *psz_pos !=' ' && *psz_pos!='/' )
557 if( *psz_pos == '/' ) // FIXME does not support multi-port
561 while( *psz_pos != '\0' && *psz_pos !=' ' )
568 *ppsz_proto = psz_pos;
569 while( *psz_pos!='\0' && *psz_pos !=' ' &&
572 *psz_pos = tolower( *psz_pos );
585 /***********************************************************************
586 * parse_sap : Takes care of the SAP headers
587 ***********************************************************************
588 * checks if the packet has the true headers ;
589 * returns the SAP header lenhth
590 ***********************************************************************/
592 static int parse_sap( char *p_packet )
594 // According to RFC 2974
595 int i_hlen = 4; // Minimum header length is 4
596 i_hlen += (p_packet[0] & 0x10) ? 16 : 4; // Address type IPv6=16bytes
597 i_hlen += p_packet[1]; // Authentification length
599 //Looks for the first '\0' byte after length
600 for(;p_packet[i_hlen]!='\0'; i_hlen++);
602 if( i_hlen > 50 ) /* Definitely too long...
603 Maybe we have a fucked up packet without \0 */
604 { /* As a workaround, we search for "v=" */
606 for(;p_packet[i_hlen] != 'v' && p_packet[i_hlen+1] != '=' ; i_hlen++);
613 /*************************************************************************
614 * packet_handle : handle the received packet and enques the
615 * the understated session
616 *************************************************************************/
618 static int packet_handle( intf_thread_t * p_intf, char *p_packet, int i_len )
621 int i_hlen; // Header length
623 i_hlen = parse_sap(p_packet);
625 if( (i_hlen > 0) && (i_hlen < i_len) )
627 p_sd = parse_sdp( p_intf, p_packet + i_hlen +1);
630 sess_toitem ( p_intf, p_sd );
636 return VLC_FALSE; // Invalid Packet
642 /***********************************************************************
643 * parse_sdp : SDP parsing
644 * *********************************************************************
645 * Make a sess_descr_t with a psz
646 ***********************************************************************/
648 static sess_descr_t * parse_sdp( intf_thread_t * p_intf, char *p_packet )
654 // According to RFC 2327, the first bytes should be exactly "v="
655 if((p_packet[0] != 'v') || (p_packet[1] != '='))
657 msg_Warn(p_intf, "Bad SDP packet");
661 if( ( sd = malloc( sizeof(sess_descr_t) ) ) == NULL )
663 msg_Err( p_intf, "Not enough memory for sd in parse_sdp()" );
668 sd->pp_attributes = NULL;
669 sd->psz_origin = NULL;
670 sd->psz_sessionname = NULL;
671 sd->psz_information = NULL;
673 sd->psz_emails = NULL;
674 sd->psz_phone = NULL;
676 sd->psz_repeat = NULL;
677 sd->psz_attribute = NULL;
678 sd->psz_connection = NULL;
682 sd->i_attributes = 0;
684 while( *p_packet != '\0' )
686 #define FIELD_COPY( p ) \
687 p = strndup( &p_packet[2], i_field_len );
692 while( *p_packet == '\n' || *p_packet == ' ' || *p_packet == '\t' )
696 if( *p_packet == '\0' )
701 if( ( psz_end = strchr( p_packet, '\n' ) ) == NULL )
703 psz_end = p_packet + strlen( p_packet );
705 i_field_len = psz_end - &p_packet[2];
707 if( p_packet[1] == '=' && i_field_len > 0)
712 FIELD_COPY( sd->psz_version );
715 FIELD_COPY( sd->psz_origin );
718 FIELD_COPY( sd->psz_sessionname );
721 FIELD_COPY( sd->psz_information );
724 FIELD_COPY( sd->psz_uri );
727 FIELD_COPY( sd->psz_emails );
730 FIELD_COPY( sd->psz_phone );
733 FIELD_COPY( sd->psz_time );
736 FIELD_COPY( sd->psz_repeat );
739 if( sd->pp_attributes )
742 realloc( sd->pp_attributes,
743 sizeof( attr_descr_t ) *
744 ( sd->i_attributes +1 ) );
748 sd->pp_attributes = malloc( sizeof( void * ) );
750 sd->pp_attributes[sd->i_attributes] =
751 malloc( sizeof( attr_descr_t ) );
753 psz_eof = strchr( p_packet, ':');
756 sd->pp_attributes[sd->i_attributes]->psz_field =
758 sd->pp_attributes[sd->i_attributes]->psz_value =
761 strlen(sd->pp_attributes[sd->i_attributes]->psz_value) ;
764 if(sd->pp_attributes[sd->i_attributes]->psz_value[i]
766 sd->pp_attributes[sd->i_attributes]->psz_value[i] =0;
775 realloc( sd->pp_media,
776 sizeof( media_descr_t ) * ( sd->i_media + 1 ) );
780 sd->pp_media = malloc( sizeof( void * ) );
784 sd->pp_media[sd->i_media] =
785 malloc( sizeof( media_descr_t ) );
786 sd->pp_media[sd->i_media]->psz_medianame = NULL;
787 sd->pp_media[sd->i_media]->psz_mediaconnection = NULL;
788 sd->pp_media[sd->i_media]->psz_medianame = strndup( &p_packet[2], i_field_len );
794 if( sd->i_media <= 0 )
796 FIELD_COPY(sd->psz_connection);
800 FIELD_COPY(sd->pp_media[sd->i_media - 1]->psz_mediaconnection);
815 if( p ) { free( p ); (p) = NULL; }
816 static void free_sd( sess_descr_t * p_sd )
821 FREE( p_sd->psz_origin );
822 FREE( p_sd->psz_sessionname );
823 FREE( p_sd->psz_information );
824 FREE( p_sd->psz_uri );
825 FREE( p_sd->psz_emails );
826 FREE( p_sd->psz_phone );
827 FREE( p_sd->psz_time );
828 FREE( p_sd->psz_repeat );
829 FREE( p_sd->psz_attribute );
830 FREE( p_sd->psz_connection );
832 for( i = 0; i < p_sd->i_media ; i++ )
834 FREE( p_sd->pp_media[i]->psz_medianame );
835 FREE( p_sd->pp_media[i]->psz_mediaconnection );
837 for( i = 0; i < p_sd->i_attributes ; i++ )
839 FREE( p_sd->pp_attributes[i]->psz_field );
840 FREE( p_sd->pp_attributes[i]->psz_value );
842 FREE( p_sd->pp_attributes );
843 FREE( p_sd->pp_media );
854 /***********************************************************************
855 * ismult: returns true if we have a multicast address
856 ***********************************************************************/
858 static int ismult( char *psz_uri )
863 i_value = strtol( psz_uri, &psz_end, 0 );
866 if( psz_uri[0] == '[')
868 if( strncasecmp( &psz_uri[1], "FF0" , 3) ||
869 strncasecmp( &psz_uri[2], "FF0" , 3))
875 if( *psz_end != '.' ) { return( VLC_FALSE ); }
877 return( i_value < 224 ? VLC_FALSE : VLC_TRUE );
882 /*****************************************************************************
883 * Read: read on a file descriptor, checking b_die periodically
884 *****************************************************************************
886 ******************************************************************************/
887 static ssize_t NetRead( intf_thread_t *p_intf,
888 int i_handle, int i_handle_v6,
889 byte_t *p_buffer, size_t i_len)
895 struct timeval timeout;
902 /* Get the max handle for select */
903 if( i_handle_v6 > i_handle )
904 i_max_handle = i_handle_v6;
906 i_max_handle = i_handle;
909 /* Initialize file descriptor set */
911 if( i_handle > 0 ) FD_SET( i_handle, &fds );
912 if( i_handle_v6 > 0 ) FD_SET( i_handle_v6, &fds);
915 /* We'll wait 0.5 second if nothing happens */
917 timeout.tv_usec = 500000;
919 /* Find if some data is available */
920 i_ret = select( i_max_handle + 1, &fds,
921 NULL, NULL, &timeout );
923 if( i_ret == -1 && errno != EINTR )
925 msg_Err( p_intf, "network select error (%s)", strerror(errno) );
932 if(FD_ISSET( i_handle, &fds ))
934 i_recv = recv( i_handle, p_buffer, i_len, 0 );
939 if(FD_ISSET( i_handle_v6, &fds ))
941 i_recv = recv( i_handle_v6, p_buffer, i_len, 0 );
947 msg_Err( p_intf, "recv failed (%s)", strerror(errno) );