1 /*****************************************************************************
2 * sap.c : SAP interface module
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: sap.c,v 1.12 2003/06/16 15:39:11 zorglub 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 *, int );
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, i_read );
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 * returns the SAP header lenhth
455 ***********************************************************************/
457 static int parse_sap( char *p_packet )
459 // According to RFC 2974
460 int i_hlen = 4; // Minimum header length is 4
461 i_hlen += (p_packet[0] & 0x10) ? 16 : 4; // Address type IPv6=16bytes
462 i_hlen += p_packet[1]; // Authentification length
464 //Looks for the first '\0' byte after length
465 for(;p_packet[i_hlen]!='\0'; i_hlen++);
470 /*************************************************************************
471 * packet_handle : handle the received packet and enques the
472 * the understated session
473 *************************************************************************/
475 static int packet_handle( intf_thread_t * p_intf, char *p_packet, int i_len )
478 int i_hlen; // Header length
480 i_hlen = parse_sap(p_packet);
482 if( (i_hlen > 0) && (i_hlen < i_len) )
484 p_sd = parse_sdp( p_intf, p_packet + i_hlen +1);
487 sess_toitem ( p_intf, p_sd );
493 return VLC_FALSE; // Invalid Packet
499 /***********************************************************************
500 * parse_sdp : SDP parsing
501 * *********************************************************************
502 * Make a sess_descr_t with a psz
503 ***********************************************************************/
505 static sess_descr_t * parse_sdp( intf_thread_t * p_intf, char *p_packet )
509 // According to RFC 2327, the first bytes should be exactly "v="
510 if((p_packet[0] != 'v') || (p_packet[1] != '='))
512 msg_Warn(p_intf, "Bad SDP packet");
516 if( ( sd = malloc( sizeof(sess_descr_t) ) ) == NULL )
518 msg_Err( p_intf, "Not enough memory for sd in parse_sdp()" );
523 sd->psz_origin = NULL;
524 sd->psz_sessionname = NULL;
525 sd->psz_information = NULL;
527 sd->psz_emails = NULL;
528 sd->psz_phone = NULL;
530 sd->psz_repeat = NULL;
531 sd->psz_attribute = NULL;
532 sd->psz_connection = NULL;
536 while( *p_packet != '\0' )
538 #define FIELD_COPY( p ) \
539 p = strndup( &p_packet[2], i_field_len );
544 while( *p_packet == '\n' || *p_packet == ' ' || *p_packet == '\t' )
548 if( *p_packet == '\0' )
553 if( ( psz_end = strchr( p_packet, '\n' ) ) == NULL )
555 psz_end = p_packet + strlen( p_packet );
557 i_field_len = psz_end - &p_packet[2];
559 if( p_packet[1] == '=' && i_field_len > 0)
564 FIELD_COPY( sd->psz_version );
567 FIELD_COPY( sd->psz_origin );
570 FIELD_COPY( sd->psz_sessionname );
573 FIELD_COPY( sd->psz_information );
576 FIELD_COPY( sd->psz_uri );
579 FIELD_COPY( sd->psz_emails );
582 FIELD_COPY( sd->psz_phone );
585 FIELD_COPY( sd->psz_time );
588 FIELD_COPY( sd->psz_repeat );
591 FIELD_COPY( sd->psz_attribute );
598 realloc( sd->pp_media,
599 sizeof( media_descr_t ) * ( sd->i_media + 1 ) );
603 sd->pp_media = malloc( sizeof( void * ) );
606 sd->pp_media[sd->i_media] =
607 malloc( sizeof( media_descr_t ) );
608 sd->pp_media[sd->i_media]->psz_medianame = NULL;
609 sd->pp_media[sd->i_media]->psz_mediaconnection = NULL;
610 sd->pp_media[sd->i_media]->psz_medianame = strndup( &p_packet[2], i_field_len );
616 if( sd->i_media <= 0 )
618 FIELD_COPY(sd->psz_connection);
622 FIELD_COPY(sd->pp_media[sd->i_media - 1]->psz_mediaconnection);
637 if( p ) { free( p ); (p) = NULL; }
638 static void free_sd( sess_descr_t * p_sd )
643 FREE( p_sd->psz_origin );
644 FREE( p_sd->psz_sessionname );
645 FREE( p_sd->psz_information );
646 FREE( p_sd->psz_uri );
647 FREE( p_sd->psz_emails );
648 FREE( p_sd->psz_phone );
649 FREE( p_sd->psz_time );
650 FREE( p_sd->psz_repeat );
651 FREE( p_sd->psz_attribute );
652 FREE( p_sd->psz_connection );
654 for( i = 0; i < p_sd->i_media ; i++ )
656 FREE( p_sd->pp_media[i]->psz_medianame );
657 FREE( p_sd->pp_media[i]->psz_mediaconnection );
659 FREE( p_sd->pp_media );
670 /***********************************************************************
671 * ismult: returns true if we have a multicast address
672 ***********************************************************************/
674 static int ismult( char *psz_uri )
679 i_value = strtol( psz_uri, &psz_end, 0 );
681 /* FIXME: This is an ugly way to detect IPv6 and to say that this is
683 if( psz_uri[0] == '[') { return( VLC_TRUE ); }
685 if( *psz_end != '.' ) { return( VLC_FALSE ); }
687 return( i_value < 224 ? VLC_FALSE : VLC_TRUE );
690 /*****************************************************************************
691 * Read: read on a file descriptor, checking b_die periodically
692 *****************************************************************************
694 ******************************************************************************/
695 static ssize_t NetRead( intf_thread_t *p_intf,
696 int i_handle, byte_t *p_buffer, size_t i_len)
702 struct timeval timeout;
706 /* Initialize file descriptor set */
708 FD_SET( i_handle, &fds );
710 /* We'll wait 0.5 second if nothing happens */
712 timeout.tv_usec = 500000;
714 /* Find if some data is available */
715 i_ret = select( i_handle + 1, &fds,
716 NULL, NULL, &timeout );
718 if( i_ret == -1 && errno != EINTR )
720 msg_Err( p_intf, "network select error (%s)", strerror(errno) );
724 ssize_t i_recv = recv( i_handle, p_buffer, i_len, 0 );
728 msg_Err( p_intf, "recv failed (%s)", strerror(errno) );