]> git.sesse.net Git - vlc/blob - modules/misc/sap.c
* include/vlc_playlist.h
[vlc] / modules / misc / sap.c
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 $
6  *
7  * Authors: Arnaud Schauly <gitan@via.ecp.fr>
8  *          ClĂ©ment Stenac <zorglub@via.ecp.fr>
9  *          Damien Lucas <nitrox@videolan.org>
10  *
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.
15  *
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.
20  *
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  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <stdlib.h>                                      /* malloc(), free() */
30 #include <string.h>
31
32 #include <errno.h>                                                 /* ENOMEM */
33 #include <stdio.h>
34 #include <ctype.h>
35 #include <signal.h>
36
37 #include <vlc/vlc.h>
38 #include <vlc/intf.h>
39 #include <vlc/vout.h>
40
41 #ifdef HAVE_UNISTD_H
42 #    include <unistd.h>
43 #endif
44
45 #ifdef HAVE_SYS_TIME_H
46 #    include <sys/time.h>
47 #endif
48 #include <sys/types.h>
49
50
51 #ifdef WIN32
52 #   include <winsock2.h>
53 #   include <ws2tcpip.h>
54 #   ifndef IN_MULTICAST
55 #       define IN_MULTICAST(a) IN_CLASSD(a)
56 #   endif
57 #else
58 #   include <sys/socket.h>
59 #   include <netinet/in.h>
60 #   if HAVE_ARPA_INET_H
61 #      include <arpa/inet.h>
62 #   elif defined( SYS_BEOS )
63 #      include <net/netdb.h>
64 #   endif
65 #endif
66
67 #ifdef UNDER_CE
68 #   define close(a) CloseHandle(a);
69 #elif defined( WIN32 )
70 #   define close(a) closesocket(a);
71 #endif
72
73 #include "network.h"
74
75 #define MAX_LINE_LENGTH 256
76
77 /* SAP is always on that port */
78 #define HELLO_PORT 9875
79 #define HELLO_GROUP "224.2.127.254"
80 #define ADD_SESSION 1
81
82 #define IPV6_ADDR_1 "FF0"  /* Scope is inserted between them */
83 #define IPV6_ADDR_2 "::2:7FFE"
84
85 /*****************************************************************************
86  * Local prototypes
87  *****************************************************************************/
88
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;
92
93 static int  Activate     ( vlc_object_t * );
94 static void Run          ( intf_thread_t *p_intf );
95 static int Kill          ( intf_thread_t * );
96
97 static ssize_t NetRead    ( intf_thread_t*, int, int , byte_t *, size_t );
98
99 /* playlist related functions */
100 static int  sess_toitem( intf_thread_t *, sess_descr_t * );
101
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 * ) ;
106
107 /* specific sdp fields parsing */
108
109 static void cfield_parse( char *, char ** );
110 static void mfield_parse( char *psz_mfield, char **ppsz_proto,
111                char **ppsz_port );
112
113 static void free_sd( sess_descr_t * );
114
115 /* Detect multicast addresses */
116 static int  ismult( char * );
117
118 /* The struct that contains sdp informations */
119 struct  sess_descr_t 
120 {
121     char *psz_version;
122     char *psz_origin;
123     char *psz_sessionname;
124     char *psz_information;
125     char *psz_uri;
126     char *psz_emails;
127     char *psz_phone;
128     char *psz_time;
129     char *psz_repeat;
130     char *psz_attribute;
131     char *psz_connection;
132     int  i_media;
133     int  i_attributes;
134     media_descr_t ** pp_media;
135     attr_descr_t ** pp_attributes;
136 };
137
138 /* All this informations are not useful yet.  */
139 struct media_descr_t 
140 {
141     char *psz_medianame;
142     char *psz_mediaconnection;
143 };
144
145 struct attr_descr_t 
146 {
147     char *psz_field;
148     char *psz_value;
149 };
150
151 /*****************************************************************************
152  * Module descriptor
153  *****************************************************************************/
154
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" )
166
167 vlc_module_begin();
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 );
171
172         add_bool( "no-sap-ipv4", 0 , NULL,
173                      SAP_IPV4_TEXT,SAP_IPV4_LONGTEXT, VLC_TRUE);
174
175         add_bool( "sap-ipv6", 0 , NULL,
176                    SAP_IPV6_TEXT, SAP_IPV6_LONGTEXT, VLC_TRUE);
177
178         add_string( "sap-ipv6-scope", "8" , NULL,
179                     SAP_SCOPE_TEXT, SAP_SCOPE_LONGTEXT, VLC_TRUE);
180
181         add_integer( "sap-group-id", 42, NULL,
182                      SAP_GROUP_ID_TEXT, SAP_GROUP_ID_LONGTEXT, VLC_TRUE);
183
184     set_description( _("SAP interface") );
185     set_capability( "interface", 0 );
186     set_callbacks( Activate, NULL);
187 vlc_module_end();
188
189 /*****************************************************************************
190  * Activate: initialize and create stuff
191  *****************************************************************************/
192 static int Activate( vlc_object_t *p_this )
193 {
194     intf_thread_t *p_intf = (intf_thread_t*)p_this;
195
196     p_intf->pf_run = Run;
197
198     return VLC_SUCCESS;
199 }
200
201 /*****************************************************************************
202  * Run: sap thread
203  *****************************************************************************
204  * Listens to SAP packets, and sends them to packet_handle
205  *****************************************************************************/
206 #define MAX_SAP_BUFFER 2000
207
208 static void Run( intf_thread_t *p_intf )
209 {
210     char *psz_addr;
211     char *psz_addrv6;
212     char *psz_network = NULL;
213     int fd            = - 1;
214     int fdv6          = -1;
215     
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" );
219     
220     char buffer[MAX_SAP_BUFFER + 1];
221
222     module_t            *p_network;
223     network_socket_t    socket_desc;
224
225     if( no_sap_ipv4 == -1 || sap_ipv6 == -1 || sap_ipv6_scope == NULL )
226     {
227         msg_Warn( p_intf, "Unable to parse module configuration" );
228         return;
229     }
230     
231     /* Prepare IPv4 Networking */
232     if ( no_sap_ipv4 == 0)
233     {
234         if( !(psz_addr = config_GetPsz( p_intf, "sap-addr" ) ) )
235         { 
236             psz_addr = strdup( HELLO_GROUP );
237         } 
238             
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;
247
248         psz_network = "ipv4"; 
249
250        /* Create, Bind the socket, ... with the appropriate module  */
251  
252         if( !( p_network = module_Need( p_intf, "network", psz_network ) ) )
253         {
254             msg_Warn( p_intf, "failed to open a connection (udp)" );
255             return;
256         }
257         module_Unneed( p_intf, p_network );
258
259         fd = socket_desc.i_handle;
260     }
261
262     /* Prepare IPv6 Networking */
263     if ( sap_ipv6 > 0)
264     {
265         /* Prepare the network_socket_t structure */
266
267         psz_addrv6=(char *)malloc(sizeof(char)*38); 
268         /* Max size of an IPv6 address */
269         
270         sprintf(psz_addrv6,"[%s%c%s]",IPV6_ADDR_1,
271                         sap_ipv6_scope[0],IPV6_ADDR_2);
272         
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;
280
281         psz_network = "ipv6"; 
282
283        /* Create, Bind the socket, ... with the appropriate module  */
284  
285         if( !( p_network = module_Need( p_intf, "network", psz_network ) ) )
286         {
287             msg_Warn( p_intf, "failed to open a connection (udp)" );
288             return;
289         }
290         module_Unneed( p_intf, p_network );
291
292         fdv6 = socket_desc.i_handle;
293     }
294
295     
296     /* read SAP packets */
297     while( !p_intf->b_die )
298     {
299         int i_read;
300
301         //memset( buffer, 0, MAX_SAP_BUFFER + 1);
302
303         i_read = NetRead( p_intf, fd, fdv6, buffer, MAX_SAP_BUFFER );
304
305         if( i_read < 0 )
306         {
307             msg_Err( p_intf, "Cannot read in the socket" );
308         }
309         if( i_read == 0 )
310         {
311             continue;
312         }
313         buffer[i_read] = '\0';
314
315         packet_handle( p_intf, buffer, i_read );
316
317     }
318
319     /* Closing socket */
320     close( socket_desc.i_handle );
321 }
322
323 /********************************************************************
324  * Kill
325  *******************************************************************
326  * Kills the SAP interface.
327  ********************************************************************/
328 static int Kill( intf_thread_t *p_intf )
329 {
330
331     p_intf->b_die = VLC_TRUE;
332
333     return VLC_SUCCESS;
334 }
335
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  *******************************************************************/
343
344 static int sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
345 {
346     playlist_item_t * p_item;
347     char *psz_uri, *psz_proto;
348     char *psz_port;
349     char *psz_uri_default;
350     int i_multicast;
351     int i_count , i;
352     vlc_bool_t b_http = VLC_FALSE;
353     char *psz_http_path = NULL;
354     playlist_t *p_playlist;
355
356     psz_uri_default = NULL;
357     cfield_parse( p_sd->psz_connection, &psz_uri_default );
358
359     for( i_count = 0 ; i_count < p_sd->i_media ; i_count++ )
360     {
361         p_item = malloc( sizeof( playlist_item_t ) );
362         if( p_item == NULL )
363         {
364             msg_Err( p_intf, "Not enough memory for p_item in sesstoitem()" );
365             return 0;
366         }
367         p_item->psz_name = strdup( p_sd->psz_sessionname );
368         p_item->i_type = 0;
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;
374
375         psz_uri = NULL;
376
377         /* Build what we have to put in p_item->psz_uri, with the m and
378          *  c fields  */
379
380         if( !p_sd->pp_media[i_count] )
381         {
382             return 0;
383         }
384
385         mfield_parse( p_sd->pp_media[i_count]->psz_medianame,
386                         & psz_proto, & psz_port );
387
388         if( !psz_proto || !psz_port )
389         {
390             return 0;
391         }
392
393         if( p_sd->pp_media[i_count]->psz_mediaconnection )
394         {
395             cfield_parse( p_sd->pp_media[i_count]->psz_mediaconnection,
396                             & psz_uri );
397         }
398         else
399         {
400             psz_uri = psz_uri_default;
401         }
402
403         if( psz_uri == NULL )
404         {
405             return 0;
406         }
407
408         for( i = 0 ; i< p_sd->i_attributes ; i++ )
409         {
410             if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "type") &&
411                 strstr( p_sd->pp_attributes[i]->psz_value, "http") )
412             {
413                 b_http = VLC_TRUE;
414             }
415             if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "http-path"))
416             {
417                 psz_http_path = strdup(  p_sd->pp_attributes[i]->psz_value );
418                 
419             }
420         }
421
422
423         /* Filling p_item->psz_uri */
424         if( b_http == VLC_FALSE )
425         {
426             i_multicast = ismult( psz_uri );
427
428             p_item->psz_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
429                         strlen( psz_port ) + 5 +i_multicast );
430
431             if( p_item->psz_uri == NULL )
432             {
433                 msg_Err( p_intf, "Not enough memory");
434                 free( p_item );
435                 return 0;
436             }
437
438             if( i_multicast == 1)
439             {
440                 sprintf( p_item->psz_uri, "%s://@%s:%s", psz_proto,
441                                  psz_uri, psz_port );
442             }
443             else
444             {
445                 sprintf( p_item->psz_uri, "%s://%s:%s", psz_proto,
446                             psz_uri, psz_port );
447             }
448         }
449         else
450         {
451             if( psz_http_path == NULL )
452                 psz_http_path = strdup("/");
453
454             p_item->psz_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
455                         strlen( psz_port ) + 3 + strlen(psz_http_path) );
456
457             if( p_item->psz_uri == NULL )
458             {
459                 msg_Err( p_intf, "Not enough memory");
460                 free( p_item );
461                 return 0;
462             }
463
464             sprintf( p_item->psz_uri, "%s://%s:%s%s", psz_proto,
465                             psz_uri, psz_port,psz_http_path );
466
467         }
468             /* Enqueueing p_item in the playlist */
469
470         if( p_item )
471         {
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 );
477
478             playlist_AddItem ( p_playlist, p_item,
479             PLAYLIST_CHECK_INSERT, PLAYLIST_END);
480             vlc_object_release( p_playlist );
481         }
482
483         if( psz_http_path )
484             free(psz_http_path);
485     }
486
487     return 1;
488
489 }
490
491 /**********************************************************************
492  * cfield_parse
493  *********************************************************************
494  * put into *ppsz_uri, the the uri in the cfield, psz_cfield.
495  *********************************************************************/
496
497 static void cfield_parse( char *psz_cfield, char **ppsz_uri )
498 {
499
500     char *psz_pos;
501     if( psz_cfield )
502     {
503         psz_pos = psz_cfield;
504
505         while( *psz_pos != ' ' && *psz_pos !='\0' )
506         {
507             psz_pos++;
508         }
509         psz_pos++;
510         while( *psz_pos != ' ' && *psz_pos !='\0' )
511         {
512             psz_pos++;
513         }
514         psz_pos++;
515         *ppsz_uri = psz_pos;
516         while( *psz_pos != ' ' && *psz_pos !='/'
517                         && *psz_pos != '\0' )
518         {
519             psz_pos++;
520         }
521         *psz_pos = '\0';
522
523     }
524     else
525     {
526         ppsz_uri = NULL;
527     }
528
529     return;
530
531 }
532
533 /**********************************************************************
534  * mfield_parse
535  *********************************************************************
536  * put into *ppsz_proto, and *ppsz_port, the protocol and the port.
537  *********************************************************************/
538
539
540 static void mfield_parse( char *psz_mfield, char **ppsz_proto,
541                char **ppsz_port )
542 {
543     char *psz_pos;
544     if( psz_mfield )
545     {
546         psz_pos = psz_mfield;
547         while( *psz_pos != '\0' && *psz_pos != ' ' )
548         {
549             psz_pos++;
550         }
551         psz_pos++;
552         *ppsz_port = psz_pos;
553         while( *psz_pos != '\0' && *psz_pos && *psz_pos !=' ' && *psz_pos!='/' )
554         {
555             psz_pos++;
556         }
557         if( *psz_pos == '/' )  // FIXME does not support multi-port
558         {
559             *psz_pos = '\0';
560             psz_pos++;
561             while( *psz_pos != '\0' && *psz_pos !=' ' )
562             {
563             psz_pos++;
564             }
565         }
566         *psz_pos = '\0';
567         psz_pos++;
568         *ppsz_proto = psz_pos;
569         while( *psz_pos!='\0' && *psz_pos !=' ' &&
570                         *psz_pos!='/' )
571         {
572             *psz_pos = tolower( *psz_pos );
573             psz_pos++;
574         }
575         *psz_pos = '\0';
576     }
577     else
578     {
579         *ppsz_proto = NULL;
580         *ppsz_port = NULL;
581     }
582     return;
583 }
584
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  ***********************************************************************/
591
592 static int parse_sap( char *p_packet )
593 {
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
598
599     //Looks for the first '\0' byte after length
600     for(;p_packet[i_hlen]!='\0'; i_hlen++);
601
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=" */
605         i_hlen = 4;
606         for(;p_packet[i_hlen] != 'v' && p_packet[i_hlen+1] != '=' ; i_hlen++);
607         return i_hlen-1;
608     }
609      
610     return(i_hlen);
611 }
612
613 /*************************************************************************
614  * packet_handle : handle the received packet and enques the
615  * the understated session
616  *************************************************************************/
617
618 static int packet_handle( intf_thread_t * p_intf, char *p_packet, int i_len )
619 {
620     sess_descr_t * p_sd;
621     int i_hlen;                             // Header length
622
623     i_hlen = parse_sap(p_packet);
624
625     if( (i_hlen > 0) && (i_hlen < i_len) )
626     {
627         p_sd = parse_sdp( p_intf, p_packet + i_hlen +1);
628         if(p_sd)
629         {
630             sess_toitem ( p_intf, p_sd );
631             free_sd ( p_sd );
632             return VLC_TRUE;
633         }
634     }
635
636     return VLC_FALSE; // Invalid Packet
637 }
638
639
640
641
642 /***********************************************************************
643  * parse_sdp : SDP parsing
644  * *********************************************************************
645  * Make a sess_descr_t with a psz
646  ***********************************************************************/
647
648 static sess_descr_t *  parse_sdp( intf_thread_t * p_intf, char *p_packet )
649 {
650     sess_descr_t *  sd;
651     char *psz_eof;
652    
653     unsigned int i; 
654     // According to RFC 2327, the first bytes should be exactly "v="
655     if((p_packet[0] != 'v') || (p_packet[1] != '='))
656     {
657         msg_Warn(p_intf, "Bad SDP packet");
658         return NULL;
659     }
660
661     if( ( sd = malloc( sizeof(sess_descr_t) ) ) == NULL )
662     {
663         msg_Err( p_intf, "Not enough memory for sd in parse_sdp()" );
664         return( NULL );
665     }
666
667     sd->pp_media = NULL;
668     sd->pp_attributes = NULL;
669     sd->psz_origin = NULL;
670     sd->psz_sessionname = NULL;
671     sd->psz_information = NULL;
672     sd->psz_uri = NULL;
673     sd->psz_emails = NULL;
674     sd->psz_phone = NULL;
675     sd->psz_time = NULL;
676     sd->psz_repeat = NULL;
677     sd->psz_attribute = NULL;
678     sd->psz_connection = NULL;
679     
680     
681     sd->i_media      = 0;
682     sd->i_attributes = 0;
683     
684     while( *p_packet != '\0'  )
685     {
686 #define FIELD_COPY( p ) \
687         p = strndup( &p_packet[2], i_field_len );
688
689         char *psz_end;
690         int  i_field_len;
691
692         while( *p_packet == '\n' || *p_packet == ' ' || *p_packet == '\t' )
693         {
694             p_packet++;
695         }
696         if( *p_packet == '\0' )
697         {
698             break;
699         }
700
701         if( ( psz_end = strchr( p_packet, '\n' ) ) == NULL )
702         {
703             psz_end = p_packet + strlen( p_packet );
704         }
705         i_field_len = psz_end - &p_packet[2];
706
707         if( p_packet[1] == '=' && i_field_len > 0)
708         {
709             switch( *p_packet )
710             {
711                 case( 'v' ):
712                     FIELD_COPY( sd->psz_version );
713                     break;
714                 case ( 'o' ):
715                     FIELD_COPY( sd->psz_origin );
716                     break;
717                 case( 's' ):
718                     FIELD_COPY( sd->psz_sessionname );
719                     break;
720                 case( 'i' ):
721                     FIELD_COPY( sd->psz_information );
722                     break;
723                 case( 'u' ):
724                     FIELD_COPY( sd->psz_uri );
725                     break;
726                 case( 'e' ):
727                     FIELD_COPY( sd->psz_emails );
728                     break;
729                 case( 'p' ):
730                     FIELD_COPY( sd->psz_phone );
731                     break;
732                 case( 't' ):
733                     FIELD_COPY( sd->psz_time );
734                     break;
735                 case( 'r' ):
736                     FIELD_COPY( sd->psz_repeat );
737                     break;
738                 case( 'a' ):
739                     if( sd->pp_attributes )
740                     {
741                         sd->pp_attributes =
742                             realloc( sd->pp_attributes,
743                                     sizeof( attr_descr_t ) * 
744                                     ( sd->i_attributes +1 ) );
745                     }
746                     else
747                     {
748                         sd->pp_attributes = malloc( sizeof( void * ) );
749                     }
750                     sd->pp_attributes[sd->i_attributes] =
751                             malloc( sizeof( attr_descr_t ) );
752                     p_packet += 2;
753                     psz_eof = strchr( p_packet, ':');
754                     if(psz_eof)
755                         *psz_eof = '\0';
756                     sd->pp_attributes[sd->i_attributes]->psz_field =
757                             strdup( p_packet );
758                     sd->pp_attributes[sd->i_attributes]->psz_value = 
759                             strdup( ++psz_eof );
760                     for( i=0 ; i< 
761                       strlen(sd->pp_attributes[sd->i_attributes]->psz_value) ;
762                              i++ )
763                     {
764                         if(sd->pp_attributes[sd->i_attributes]->psz_value[i]
765                                         =='\n' )
766                           sd->pp_attributes[sd->i_attributes]->psz_value[i]                                            =0;                    
767                     }
768                     sd->i_attributes++;
769                     break;
770
771                 case( 'm' ):
772                     if( sd->pp_media )
773                     {
774                         sd->pp_media =
775                             realloc( sd->pp_media,
776                                sizeof( media_descr_t ) * ( sd->i_media + 1 ) );
777                     }
778                     else
779                     {
780                         sd->pp_media = malloc( sizeof( void * ) );
781                     }
782
783                     
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 );
789
790                     sd->i_media++;
791                     break;
792
793                 case( 'c' ):
794                     if( sd->i_media <= 0 )
795                     {
796                         FIELD_COPY(sd->psz_connection);
797                     }
798                     else
799                     {
800                         FIELD_COPY(sd->pp_media[sd->i_media - 1]->psz_mediaconnection);
801                     }
802                    break;
803                 default:
804                    break;
805             }
806         }
807         p_packet = psz_end;
808 #undef FIELD_COPY
809     }
810
811     return sd;
812 }
813
814 #define FREE( p ) \
815     if( p ) { free( p ); (p) = NULL; }
816 static void free_sd( sess_descr_t * p_sd )
817 {
818     int i;
819     if( p_sd )
820     {
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 );
831
832         for( i = 0; i < p_sd->i_media ; i++ )
833         {
834             FREE( p_sd->pp_media[i]->psz_medianame );
835             FREE( p_sd->pp_media[i]->psz_mediaconnection );
836         }
837         for( i = 0; i < p_sd->i_attributes ; i++ )
838         {
839             FREE( p_sd->pp_attributes[i]->psz_field );
840             FREE( p_sd->pp_attributes[i]->psz_value );
841         }
842         FREE( p_sd->pp_attributes );
843         FREE( p_sd->pp_media );
844
845         free( p_sd );
846     }
847     else
848     {
849         ;
850     }
851     return;
852 }
853
854 /***********************************************************************
855  * ismult: returns true if we have a multicast address
856  ***********************************************************************/
857
858 static int ismult( char *psz_uri )
859 {
860     char *psz_end;
861     int  i_value;
862
863     i_value = strtol( psz_uri, &psz_end, 0 );
864
865     /* IPv6 */
866     if( psz_uri[0] == '[') 
867     {
868       if( strncasecmp( &psz_uri[1], "FF0" , 3) || 
869           strncasecmp( &psz_uri[2], "FF0" , 3))
870             return( VLC_TRUE );
871         else
872             return( VLC_FALSE ); 
873     } 
874
875     if( *psz_end != '.' ) { return( VLC_FALSE ); }
876
877     return( i_value < 224 ? VLC_FALSE : VLC_TRUE );
878 }
879
880
881
882 /*****************************************************************************
883  * Read: read on a file descriptor, checking b_die periodically
884  *****************************************************************************
885  * Taken from udp.c
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)
890 {
891 #ifdef UNDER_CE
892     return -1;
893
894 #else
895     struct timeval  timeout;
896     fd_set          fds;
897     int             i_ret;
898     int             i_max_handle;
899
900     ssize_t i_recv=-1;
901     
902     /* Get the max handle for select */
903     if( i_handle_v6 > i_handle )
904         i_max_handle = i_handle_v6;
905     else
906         i_max_handle = i_handle;
907
908     
909     /* Initialize file descriptor set */
910     FD_ZERO( &fds );
911     if(   i_handle > 0   ) FD_SET( i_handle, &fds );
912     if( i_handle_v6  > 0 ) FD_SET( i_handle_v6, &fds);
913
914
915     /* We'll wait 0.5 second if nothing happens */
916     timeout.tv_sec = 0;
917     timeout.tv_usec = 500000;
918
919     /* Find if some data is available */
920     i_ret = select( i_max_handle + 1, &fds,
921     NULL, NULL, &timeout );
922
923     if( i_ret == -1 && errno != EINTR )
924     {
925         msg_Err( p_intf, "network select error (%s)", strerror(errno) );
926     }
927     else if( i_ret > 0 )
928    {
929       /* Get the data */     
930       if(i_handle >0)
931       {
932          if(FD_ISSET( i_handle, &fds ))
933          {
934              i_recv = recv( i_handle, p_buffer, i_len, 0 );
935          }
936       }
937       if(i_handle_v6 >0)
938       {
939          if(FD_ISSET( i_handle_v6, &fds ))
940          {
941             i_recv = recv( i_handle_v6, p_buffer, i_len, 0 );
942          }
943       }
944       
945        if( i_recv < 0 )
946         {
947            msg_Err( p_intf, "recv failed (%s)", strerror(errno) );
948         }
949         return i_recv;
950     }
951
952     return 0;
953
954 #endif
955 }
956