1 /*****************************************************************************
2 * sap.c : SAP interface module
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: sap.c,v 1.7 2003/01/21 18:43:54 lool 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( char *,intf_thread_t * ) ;
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 );
132 add_string( "sap-addr", NULL, NULL,
133 "SAP multicast address", "SAP multicast address" );
134 set_description( _("SAP interface module") );
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 *****************************************************************************/
157 static void Run( intf_thread_t *p_intf )
160 char *psz_network = NULL;
161 struct sockaddr_in addr;
166 network_socket_t socket_desc;
170 if( !(psz_addr = config_GetPsz( p_intf, "sap-addr" ) ) )
172 psz_addr = strdup( HELLO_GROUP );
175 /* Prepare the network_socket_t structure */
176 socket_desc.i_type = NETWORK_UDP;
177 socket_desc.psz_bind_addr = psz_addr;
178 socket_desc.i_bind_port = HELLO_PORT;
179 socket_desc.psz_server_addr = "";
180 socket_desc.i_server_port = 0;
181 p_intf->p_private = (void*) &socket_desc;
183 psz_network = "ipv4"; // FIXME
185 /* Create, Bind the socket, ... with the appropriate module */
187 if( !( p_network = module_Need( p_intf, "network", psz_network ) ) )
189 msg_Warn( p_intf, "failed to open a connection (udp)" );
192 module_Unneed( p_intf, p_network );
194 fd = socket_desc.i_handle;
197 /* read SAP packets */
199 psz_buf = malloc( 2000 );
200 if( psz_buf == NULL )
202 msg_Err( p_intf, "Not enough memory for psz_buf in Run()" );
206 while( !p_intf->b_die )
209 addrlen=sizeof(addr);
212 memset(psz_buf, 0, 2000);
214 i_read = NetRead( p_intf, fd, psz_buf, 2000 );
218 msg_Err( p_intf, "Cannot read in the socket" );
226 packet_handle( p_intf, &psz_buf );
234 CloseHandle( socket_desc.i_handle );
235 #elif defined( WIN32 )
236 closesocket( socket_desc.i_handle );
238 close( socket_desc.i_handle );
243 /********************************************************************
245 *******************************************************************
246 * Kills the SAP interface.
247 ********************************************************************/
248 static int Kill( intf_thread_t *p_intf )
251 p_intf->b_die = VLC_TRUE;
256 /*******************************************************************
257 * sess_toitem : changes a sess_descr_t into a hurd of
258 * playlist_item_t, which are enqueued.
259 *******************************************************************
260 * Note : does not support sessions that take place on consecutive
261 * port or adresses yet.
262 *******************************************************************/
264 static int sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
266 playlist_item_t * p_item;
267 char *psz_uri, *psz_proto;
269 char *psz_uri_default;
272 playlist_t *p_playlist;
274 psz_uri_default = NULL;
275 cfield_parse( p_sd->psz_connection, &psz_uri_default );
277 for( i_count=0 ; i_count <= p_sd->i_media ; i_count ++ )
279 p_item = malloc( sizeof( playlist_item_t ) );
282 msg_Err( p_intf, "Not enough memory for p_item in sesstoitem()" );
285 p_item->psz_name = strdup( p_sd->psz_sessionname );
287 p_item->i_status = 0;
288 p_item->b_autodeletion = VLC_FALSE;
289 p_item->psz_uri = NULL;
293 /* Build what we have to put in p_item->psz_uri, with the m and
296 if( !p_sd->pp_media[i_count] )
301 mfield_parse( p_sd->pp_media[i_count]->psz_medianame,
302 & psz_proto, & psz_port );
304 if( !psz_proto || !psz_port )
309 if( p_sd->pp_media[i_count]->psz_mediaconnection )
311 cfield_parse( p_sd->pp_media[i_count]->psz_mediaconnection,
319 if( psz_uri == NULL )
321 if( psz_uri_default )
323 psz_uri = psz_uri_default;
332 /* Filling p_item->psz_uri */
333 i_multicast = ismult( psz_uri );
335 p_item->psz_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
336 strlen( psz_port ) + 5 +i_multicast );
337 if( p_item->psz_uri == NULL )
339 msg_Err( p_intf, "Not enough memory");
344 if( i_multicast == 1)
346 sprintf( p_item->psz_uri, "%s://@%s:%s", psz_proto,
351 sprintf( p_item->psz_uri, "%s://%s:%s", psz_proto,
355 /* Enqueueing p_item in the playlist */
359 p_playlist = vlc_object_find( p_intf,
360 VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
362 playlist_AddItem ( p_playlist, p_item,
363 PLAYLIST_CHECK_INSERT, PLAYLIST_END);
364 vlc_object_release( p_playlist );
375 /**********************************************************************
377 *********************************************************************
378 * put into *ppsz_uri, the the uri in the cfield, psz_cfield.
379 *********************************************************************/
381 static void cfield_parse( char *psz_cfield, char **ppsz_uri )
387 psz_pos = psz_cfield;
389 while( *psz_pos != ' ' && *psz_pos !='\0' )
394 while( *psz_pos != ' ' && *psz_pos !='\0' )
400 while( *psz_pos != ' ' && *psz_pos !='/'
401 && *psz_pos != '\0' )
417 /**********************************************************************
419 *********************************************************************
420 * put into *ppsz_proto, and *ppsz_port, the protocol and the port.
421 *********************************************************************/
424 static void mfield_parse( char *psz_mfield, char **ppsz_proto,
430 psz_pos = psz_mfield;
431 while( *psz_pos != '\0' && *psz_pos != ' ' )
436 *ppsz_port = psz_pos;
437 while( *psz_pos != '\0' && *psz_pos && *psz_pos !=' ' && *psz_pos!='/' )
441 if( *psz_pos == '/' ) // FIXME does not support multi-port
445 while( *psz_pos != '\0' && *psz_pos !=' ' )
452 *ppsz_proto = psz_pos;
453 while( *psz_pos!='\0' && *psz_pos !=' ' &&
456 *psz_pos = tolower( *psz_pos );
469 /***********************************************************************
470 * parse_sap : Takes care of the SAP headers
471 ***********************************************************************
472 * checks if the packet has the true headers ;
473 ***********************************************************************/
475 static int parse_sap( char ** ppsz_sa_packet ) { /* Dummy Parser : does nothing !*/
476 if( *ppsz_sa_packet ) return ADD_SESSION; //Add this packet
477 return 0; /* FIXME */
480 /*************************************************************************
481 * packet_handle : handle the received packet and enques the
482 * the understated session
483 *************************************************************************/
485 static int packet_handle( intf_thread_t * p_intf, char ** ppsz_packet ) {
489 j=parse_sap( ppsz_packet );
493 p_sd = parse_sdp( *ppsz_packet, p_intf );
495 sess_toitem ( p_intf, p_sd );
500 return 0; // Invalid Packet
506 /***********************************************************************
507 * parse_sdp : SDP parsing
508 * *********************************************************************
509 * Make a sess_descr_t with a psz
510 ***********************************************************************/
512 static sess_descr_t * parse_sdp( char * psz_pct, intf_thread_t * p_intf )
515 char ** ppsz_fill=NULL;
518 sd = malloc( sizeof(sess_descr_t) );
521 msg_Err( p_intf, "Not enough memory for sd in parse_sdp()" );
526 sd->psz_origin = NULL;
527 sd->psz_sessionname = NULL;
528 sd->psz_information = NULL;
530 sd->psz_emails = NULL;
531 sd->psz_phone = NULL;
533 sd->psz_repeat = NULL;
534 sd->psz_attribute = NULL;
535 sd->psz_connection = NULL;
539 while( psz_pct[j]!=EOF && psz_pct[j] != '\0' )
542 if (psz_pct[j] == '=')
544 switch(psz_pct[(j-1)]) {
546 ppsz_fill = & sd->psz_version;
550 ppsz_fill = & sd->psz_origin;
554 ppsz_fill = & sd->psz_sessionname;
558 ppsz_fill = & sd->psz_information;
562 ppsz_fill = & sd->psz_uri;
566 ppsz_fill = & sd->psz_emails;
570 ppsz_fill = & sd->psz_phone;
574 ppsz_fill = & sd->psz_time;
578 ppsz_fill = & sd->psz_repeat;
582 ppsz_fill = & sd->psz_attribute;
588 sd->pp_media = realloc( sd->pp_media,
589 ( sizeof( void * ) * (sd->i_media + 1)) );
593 sd->pp_media = malloc( sizeof ( void * ) );
594 if( sd->pp_media == NULL )
596 msg_Err( p_intf, "Not enough memory for " \
597 "sd->pp_media in parse_sdp()" );
602 sd->pp_media[sd->i_media] =
603 malloc( sizeof( media_descr_t ) );
604 if( sd->pp_media[sd->i_media] == NULL )
607 msg_Err( p_intf, "Not enough memory for " \
608 "sd->pp_media[sd->i_media] " \
614 sd->pp_media[sd->i_media]->psz_medianame = NULL;
615 sd->pp_media[sd->i_media]->psz_mediaconnection = NULL;
617 ppsz_fill = & sd->pp_media[sd->i_media]->psz_medianame;
621 if( sd->i_media == -1 )
623 ppsz_fill = & sd->psz_connection;
627 ppsz_fill = & sd->pp_media[sd->i_media]->
641 while (psz_pct[j] != '\n'&& psz_pct[j] != EOF) {
646 if( ppsz_fill != NULL )
648 *ppsz_fill = malloc( sizeof(char) * (k + 1) );
649 if( *ppsz_fill == NULL )
651 msg_Err( p_intf, "Not enough memory for " \
652 "*ppsz_fill in parse_sdp()" );
656 memcpy(*ppsz_fill, &(psz_pct[j-k+1]), k );
657 (*ppsz_fill)[k]='\0';
668 if( p ) { free( p ); (p) = NULL; }
669 static void free_sd( sess_descr_t * p_sd )
674 FREE( p_sd->psz_origin );
675 FREE( p_sd->psz_sessionname );
676 FREE( p_sd->psz_information );
677 FREE( p_sd->psz_uri );
678 FREE( p_sd->psz_emails );
679 FREE( p_sd->psz_phone );
680 FREE( p_sd->psz_time );
681 FREE( p_sd->psz_repeat );
682 FREE( p_sd->psz_attribute );
683 FREE( p_sd->psz_connection );
685 if( p_sd->i_media >= 0 && p_sd->pp_media )
687 for( i=0; i <= p_sd->i_media ; i++ )
689 FREE( p_sd->pp_media[i]->psz_medianame );
690 FREE( p_sd->pp_media[i]->psz_mediaconnection );
692 FREE( p_sd->pp_media );
703 /***********************************************************************
705 ***********************************************************************/
707 static int ismult( char *psz_uri )
715 /* msg_Err( p_intf, "Not enough memory for psz_c in ismult()" ); */
719 memcpy( psz_c, psz_uri, 3 );
720 if( psz_c[2] == '.' || psz_c[1] == '.' )
737 /*****************************************************************************
738 * Read: read on a file descriptor, checking b_die periodically
739 *****************************************************************************
741 ******************************************************************************/
742 static ssize_t NetRead( intf_thread_t *p_intf,
743 int i_handle, byte_t *p_buffer, size_t i_len)
749 struct timeval timeout;
753 /* Initialize file descriptor set */
755 FD_SET( i_handle, &fds );
757 /* We'll wait 0.5 second if nothing happens */
759 timeout.tv_usec = 500000;
761 /* Find if some data is available */
762 i_ret = select( i_handle + 1, &fds,
763 NULL, NULL, &timeout );
765 if( i_ret == -1 && errno != EINTR )
767 msg_Err( p_intf, "network select error (%s)", strerror(errno) );
771 ssize_t i_recv = recv( i_handle, p_buffer, i_len, 0 );
775 msg_Err( p_intf, "recv failed (%s)", strerror(errno) );