1 /*****************************************************************************
2 * sap.c : SAP interface module
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: sap.c,v 1.10 2003/03/30 18:14:38 gbazin Exp $
7 * Authors: Arnaud Schauly <gitan@via.ecp.fr>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
27 #include <stdlib.h> /* malloc(), free() */
30 #include <errno.h> /* ENOMEM */
43 #ifdef HAVE_SYS_TIME_H
44 # include <sys/time.h>
46 #include <sys/types.h>
50 # include <winsock2.h>
51 # include <ws2tcpip.h>
53 # define IN_MULTICAST(a) IN_CLASSD(a)
56 # include <sys/socket.h>
57 # include <netinet/in.h>
59 # include <arpa/inet.h>
60 # elif defined( SYS_BEOS )
61 # include <net/netdb.h>
67 #define MAX_LINE_LENGTH 256
69 /* SAP is always on that port */
70 #define HELLO_PORT 9875
71 #define HELLO_GROUP "224.2.127.254"
74 /*****************************************************************************
76 *****************************************************************************/
78 typedef struct media_descr_t media_descr_t;
79 typedef struct sess_descr_t sess_descr_t;
81 static int Activate ( vlc_object_t * );
82 static void Run ( intf_thread_t *p_intf );
83 static int Kill ( intf_thread_t * );
85 static ssize_t NetRead ( intf_thread_t*, int , byte_t *, size_t );
87 /* playlist related functions */
88 static int sess_toitem( intf_thread_t *, sess_descr_t * );
90 /* sap/sdp related functions */
91 static int parse_sap ( char * );
92 static int packet_handle ( intf_thread_t *, char * );
93 static sess_descr_t * parse_sdp( intf_thread_t *, char * ) ;
95 /* specific sdp fields parsing */
97 static void cfield_parse( char *, char ** );
98 static void mfield_parse( char *psz_mfield, char **ppsz_proto,
101 static void free_sd( sess_descr_t * );
102 static int ismult( char * );
104 /* The struct that contains sdp informations */
105 struct sess_descr_t {
108 char *psz_sessionname;
109 char *psz_information;
116 char *psz_connection;
118 media_descr_t ** pp_media;
121 /* All this informations are not useful yet. */
122 struct media_descr_t {
124 char *psz_mediaconnection;
127 /*****************************************************************************
129 *****************************************************************************/
131 add_category_hint( N_("SAP"), NULL, VLC_TRUE );
132 add_string( "sap-addr", NULL, NULL,
133 "SAP multicast address", "SAP multicast address", VLC_TRUE );
134 set_description( _("SAP interface") );
135 set_capability( "interface", 0 );
136 set_callbacks( Activate, NULL);
139 /*****************************************************************************
140 * Activate: initialize and create stuff
141 *****************************************************************************/
142 static int Activate( vlc_object_t *p_this )
144 intf_thread_t *p_intf = (intf_thread_t*)p_this;
146 p_intf->pf_run = Run;
151 /*****************************************************************************
153 *****************************************************************************
154 * Listens to SAP packets, and sends them to packet_handle
155 *****************************************************************************/
156 #define MAX_SAP_BUFFER 2000
158 static void Run( intf_thread_t *p_intf )
161 char *psz_network = NULL;
164 char buffer[MAX_SAP_BUFFER + 1];
167 network_socket_t socket_desc;
169 if( !(psz_addr = config_GetPsz( p_intf, "sap-addr" ) ) )
171 psz_addr = strdup( HELLO_GROUP );
174 /* Prepare the network_socket_t structure */
175 socket_desc.i_type = NETWORK_UDP;
176 socket_desc.psz_bind_addr = psz_addr;
177 socket_desc.i_bind_port = HELLO_PORT;
178 socket_desc.psz_server_addr = "";
179 socket_desc.i_server_port = 0;
180 p_intf->p_private = (void*) &socket_desc;
182 psz_network = "ipv4"; // FIXME
184 /* Create, Bind the socket, ... with the appropriate module */
186 if( !( p_network = module_Need( p_intf, "network", psz_network ) ) )
188 msg_Warn( p_intf, "failed to open a connection (udp)" );
191 module_Unneed( p_intf, p_network );
193 fd = socket_desc.i_handle;
196 /* read SAP packets */
197 while( !p_intf->b_die )
201 //memset( buffer, 0, MAX_SAP_BUFFER + 1);
203 i_read = NetRead( p_intf, fd, buffer, MAX_SAP_BUFFER );
207 msg_Err( p_intf, "Cannot read in the socket" );
213 buffer[i_read] = '\0';
215 packet_handle( p_intf, buffer );
222 CloseHandle( socket_desc.i_handle );
223 #elif defined( WIN32 )
224 closesocket( socket_desc.i_handle );
226 close( socket_desc.i_handle );
231 /********************************************************************
233 *******************************************************************
234 * Kills the SAP interface.
235 ********************************************************************/
236 static int Kill( intf_thread_t *p_intf )
239 p_intf->b_die = VLC_TRUE;
244 /*******************************************************************
245 * sess_toitem : changes a sess_descr_t into a hurd of
246 * playlist_item_t, which are enqueued.
247 *******************************************************************
248 * Note : does not support sessions that take place on consecutive
249 * port or adresses yet.
250 *******************************************************************/
252 static int sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
254 playlist_item_t * p_item;
255 char *psz_uri, *psz_proto;
257 char *psz_uri_default;
260 playlist_t *p_playlist;
262 psz_uri_default = NULL;
263 cfield_parse( p_sd->psz_connection, &psz_uri_default );
265 for( i_count = 0 ; i_count < p_sd->i_media ; i_count++ )
267 p_item = malloc( sizeof( playlist_item_t ) );
270 msg_Err( p_intf, "Not enough memory for p_item in sesstoitem()" );
273 p_item->psz_name = strdup( p_sd->psz_sessionname );
275 p_item->i_status = 0;
276 p_item->b_autodeletion = VLC_FALSE;
277 p_item->psz_uri = NULL;
281 /* Build what we have to put in p_item->psz_uri, with the m and
284 if( !p_sd->pp_media[i_count] )
289 mfield_parse( p_sd->pp_media[i_count]->psz_medianame,
290 & psz_proto, & psz_port );
292 if( !psz_proto || !psz_port )
297 if( p_sd->pp_media[i_count]->psz_mediaconnection )
299 cfield_parse( p_sd->pp_media[i_count]->psz_mediaconnection,
304 psz_uri = psz_uri_default;
307 if( psz_uri == NULL )
313 /* Filling p_item->psz_uri */
314 i_multicast = ismult( psz_uri );
316 p_item->psz_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
317 strlen( psz_port ) + 5 +i_multicast );
318 if( p_item->psz_uri == NULL )
320 msg_Err( p_intf, "Not enough memory");
325 if( i_multicast == 1)
327 sprintf( p_item->psz_uri, "%s://@%s:%s", psz_proto,
332 sprintf( p_item->psz_uri, "%s://%s:%s", psz_proto,
336 /* Enqueueing p_item in the playlist */
340 p_playlist = vlc_object_find( p_intf,
341 VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
343 playlist_AddItem ( p_playlist, p_item,
344 PLAYLIST_CHECK_INSERT, PLAYLIST_END);
345 vlc_object_release( p_playlist );
356 /**********************************************************************
358 *********************************************************************
359 * put into *ppsz_uri, the the uri in the cfield, psz_cfield.
360 *********************************************************************/
362 static void cfield_parse( char *psz_cfield, char **ppsz_uri )
368 psz_pos = psz_cfield;
370 while( *psz_pos != ' ' && *psz_pos !='\0' )
375 while( *psz_pos != ' ' && *psz_pos !='\0' )
381 while( *psz_pos != ' ' && *psz_pos !='/'
382 && *psz_pos != '\0' )
398 /**********************************************************************
400 *********************************************************************
401 * put into *ppsz_proto, and *ppsz_port, the protocol and the port.
402 *********************************************************************/
405 static void mfield_parse( char *psz_mfield, char **ppsz_proto,
411 psz_pos = psz_mfield;
412 while( *psz_pos != '\0' && *psz_pos != ' ' )
417 *ppsz_port = psz_pos;
418 while( *psz_pos != '\0' && *psz_pos && *psz_pos !=' ' && *psz_pos!='/' )
422 if( *psz_pos == '/' ) // FIXME does not support multi-port
426 while( *psz_pos != '\0' && *psz_pos !=' ' )
433 *ppsz_proto = psz_pos;
434 while( *psz_pos!='\0' && *psz_pos !=' ' &&
437 *psz_pos = tolower( *psz_pos );
450 /***********************************************************************
451 * parse_sap : Takes care of the SAP headers
452 ***********************************************************************
453 * checks if the packet has the true headers ;
454 ***********************************************************************/
456 static int parse_sap( char *p_packet )
457 { /* Dummy Parser : does nothing !*/
462 /*************************************************************************
463 * packet_handle : handle the received packet and enques the
464 * the understated session
465 *************************************************************************/
467 static int packet_handle( intf_thread_t * p_intf, char *p_packet )
471 if( parse_sap( p_packet ) )
473 p_sd = parse_sdp( p_intf, p_packet);
475 sess_toitem ( p_intf, p_sd );
481 return VLC_FALSE; // Invalid Packet
487 /***********************************************************************
488 * parse_sdp : SDP parsing
489 * *********************************************************************
490 * Make a sess_descr_t with a psz
491 ***********************************************************************/
493 static sess_descr_t * parse_sdp( intf_thread_t * p_intf, char *p_packet )
497 if( ( sd = malloc( sizeof(sess_descr_t) ) ) == NULL )
499 msg_Err( p_intf, "Not enough memory for sd in parse_sdp()" );
504 sd->psz_origin = NULL;
505 sd->psz_sessionname = NULL;
506 sd->psz_information = NULL;
508 sd->psz_emails = NULL;
509 sd->psz_phone = NULL;
511 sd->psz_repeat = NULL;
512 sd->psz_attribute = NULL;
513 sd->psz_connection = NULL;
518 while( *p_packet != '\0' )
520 #define FIELD_COPY( p ) \
521 p = strndup( &p_packet[2], i_field_len );
526 while( *p_packet == '\n' || *p_packet == ' ' || *p_packet == '\t' )
530 if( *p_packet == '\0' )
535 if( ( psz_end = strchr( p_packet, '\n' ) ) == NULL )
537 psz_end = p_packet + strlen( p_packet );
539 i_field_len = psz_end - &p_packet[2];
541 if( p_packet[1] == '=' && i_field_len > 0)
546 FIELD_COPY( sd->psz_version );
549 FIELD_COPY( sd->psz_origin );
552 FIELD_COPY( sd->psz_sessionname );
555 FIELD_COPY( sd->psz_information );
558 FIELD_COPY( sd->psz_uri );
561 FIELD_COPY( sd->psz_emails );
564 FIELD_COPY( sd->psz_phone );
567 FIELD_COPY( sd->psz_time );
570 FIELD_COPY( sd->psz_repeat );
573 FIELD_COPY( sd->psz_attribute );
580 realloc( sd->pp_media,
581 sizeof( media_descr_t ) * ( sd->i_media + 1 ) );
585 sd->pp_media = malloc( sizeof( void * ) );
588 sd->pp_media[sd->i_media] =
589 malloc( sizeof( media_descr_t ) );
590 sd->pp_media[sd->i_media]->psz_medianame = NULL;
591 sd->pp_media[sd->i_media]->psz_mediaconnection = NULL;
592 sd->pp_media[sd->i_media]->psz_medianame = strndup( &p_packet[2], i_field_len );
598 if( sd->i_media <= 0 )
600 sd->psz_connection = strndup( &p_packet[2], i_field_len );
604 sd->pp_media[sd->i_media - 1]->psz_mediaconnection = strndup( &p_packet[2], i_field_len );
619 if( p ) { free( p ); (p) = NULL; }
620 static void free_sd( sess_descr_t * p_sd )
625 FREE( p_sd->psz_origin );
626 FREE( p_sd->psz_sessionname );
627 FREE( p_sd->psz_information );
628 FREE( p_sd->psz_uri );
629 FREE( p_sd->psz_emails );
630 FREE( p_sd->psz_phone );
631 FREE( p_sd->psz_time );
632 FREE( p_sd->psz_repeat );
633 FREE( p_sd->psz_attribute );
634 FREE( p_sd->psz_connection );
636 for( i = 0; i < p_sd->i_media ; i++ )
638 FREE( p_sd->pp_media[i]->psz_medianame );
639 FREE( p_sd->pp_media[i]->psz_mediaconnection );
641 FREE( p_sd->pp_media );
652 /***********************************************************************
654 ***********************************************************************/
656 static int ismult( char *psz_uri )
661 i_value = strtol( psz_uri, &psz_end, 0 );
663 if( *psz_end != '.' )
668 return( i_value < 224 ? VLC_FALSE : VLC_TRUE );
671 /*****************************************************************************
672 * Read: read on a file descriptor, checking b_die periodically
673 *****************************************************************************
675 ******************************************************************************/
676 static ssize_t NetRead( intf_thread_t *p_intf,
677 int i_handle, byte_t *p_buffer, size_t i_len)
683 struct timeval timeout;
687 /* Initialize file descriptor set */
689 FD_SET( i_handle, &fds );
691 /* We'll wait 0.5 second if nothing happens */
693 timeout.tv_usec = 500000;
695 /* Find if some data is available */
696 i_ret = select( i_handle + 1, &fds,
697 NULL, NULL, &timeout );
699 if( i_ret == -1 && errno != EINTR )
701 msg_Err( p_intf, "network select error (%s)", strerror(errno) );
705 ssize_t i_recv = recv( i_handle, p_buffer, i_len, 0 );
709 msg_Err( p_intf, "recv failed (%s)", strerror(errno) );