]> git.sesse.net Git - vlc/blob - modules/misc/sap.c
* src/playlist/* && Makefile.am
[vlc] / modules / misc / sap.c
1 /*****************************************************************************
2  * sap.c :  SAP interface module
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: sap.c,v 1.28 2003/10/29 17:32:55 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 /* Our custom structure */
119 struct intf_sys_t
120 {
121     int i_group;
122 };
123
124 /* The struct that contains sdp informations */
125 struct  sess_descr_t
126 {
127     char *psz_version;
128     char *psz_origin;
129     char *psz_sessionname;
130     char *psz_information;
131     char *psz_uri;
132     char *psz_emails;
133     char *psz_phone;
134     char *psz_time;
135     char *psz_repeat;
136     char *psz_attribute;
137     char *psz_connection;
138     int  i_media;
139     int  i_attributes;
140     media_descr_t ** pp_media;
141     attr_descr_t ** pp_attributes;
142 };
143
144 /* All this informations are not useful yet.  */
145 struct media_descr_t
146 {
147     char *psz_medianame;
148     char *psz_mediaconnection;
149 };
150
151 struct attr_descr_t
152 {
153     char *psz_field;
154     char *psz_value;
155 };
156
157 /*****************************************************************************
158  * Module descriptor
159  *****************************************************************************/
160
161 #define SAP_ADDR_TEXT N_("SAP multicast address")
162 #define SAP_ADDR_LONGTEXT N_("SAP multicast address")
163 #define SAP_IPV4_TEXT N_("IPv4-SAP listening")
164 #define SAP_IPV4_LONGTEXT N_("Set this if you want SAP to listen for IPv4 announces")
165 #define SAP_IPV6_TEXT N_("IPv6-SAP listening")
166 #define SAP_IPV6_LONGTEXT N_("Set this if you want SAP to listen for IPv6 announces")
167 #define SAP_SCOPE_TEXT N_("IPv6 SAP scope")
168 #define SAP_SCOPE_LONGTEXT N_("Sets the scope for IPv6 announces (default is 8)")
169
170 vlc_module_begin();
171     add_category_hint( N_("SAP"), NULL, VLC_TRUE );
172         add_string( "sap-addr", NULL, NULL,
173                      SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, VLC_TRUE );
174
175         add_bool( "sap-ipv4", 1 , NULL,
176                      SAP_IPV4_TEXT,SAP_IPV4_LONGTEXT, VLC_TRUE);
177
178         add_bool( "sap-ipv6", 0 , NULL,
179                    SAP_IPV6_TEXT, SAP_IPV6_LONGTEXT, VLC_TRUE);
180
181         add_string( "sap-ipv6-scope", "8" , NULL,
182                     SAP_SCOPE_TEXT, SAP_SCOPE_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 sap_ipv4         = config_GetInt( p_intf, "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     playlist_t          *p_playlist;
225     playlist_group_t    *p_group;
226
227     if( sap_ipv4 == -1 || sap_ipv6 == -1 || sap_ipv6_scope == NULL )
228     {
229         msg_Warn( p_intf, "Unable to parse module configuration" );
230         return;
231     }
232
233
234     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
235     if( !p_intf->p_sys )
236     {
237         msg_Err( p_intf, "Out of memory !");
238         return VLC_EGENERIC;
239     }
240     /* Create our playlist group */
241     p_playlist =
242           (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
243                                                  FIND_ANYWHERE );
244     p_group = playlist_CreateGroup( p_playlist , "SAP" );
245     p_intf->p_sys->i_group = p_group->i_id;
246
247     vlc_object_release( p_playlist );
248
249     /* Prepare IPv4 Networking */
250     if ( sap_ipv4 == 1)
251     {
252         if( !(psz_addr = config_GetPsz( p_intf, "sap-addr" ) ) )
253         {
254             psz_addr = strdup( HELLO_GROUP );
255         }
256
257         /* Prepare the network_socket_t structure */
258         socket_desc.i_type            = NETWORK_UDP;
259         socket_desc.psz_bind_addr     = psz_addr;
260         socket_desc.i_bind_port       = HELLO_PORT;
261         socket_desc.psz_server_addr   = "";
262         socket_desc.i_server_port     = 0;
263         socket_desc.i_ttl             = 0;
264         p_intf->p_private = (void*) &socket_desc;
265
266         psz_network = "ipv4";
267
268        /* Create, Bind the socket, ... with the appropriate module  */
269
270         if( !( p_network = module_Need( p_intf, "network", psz_network ) ) )
271         {
272             msg_Warn( p_intf, "failed to open a connection (udp)" );
273             return;
274         }
275         module_Unneed( p_intf, p_network );
276
277         fd = socket_desc.i_handle;
278     }
279
280     /* Prepare IPv6 Networking */
281     if ( sap_ipv6 > 0)
282     {
283         /* Prepare the network_socket_t structure */
284
285         psz_addrv6=(char *)malloc(sizeof(char)*38);
286
287         if( !psz_addrv6)
288         {
289             msg_Warn( p_intf, "Out of memory !" );
290         }
291         /* Max size of an IPv6 address */
292
293         sprintf(psz_addrv6,"[%s%c%s]",IPV6_ADDR_1,
294                         sap_ipv6_scope[0],IPV6_ADDR_2);
295
296         socket_desc.i_type            = NETWORK_UDP;
297         socket_desc.psz_bind_addr     = psz_addrv6;
298         socket_desc.i_bind_port       = HELLO_PORT;
299         socket_desc.psz_server_addr   = "";
300         socket_desc.i_server_port     = 0;
301         socket_desc.i_ttl             = 0;
302         p_intf->p_private = (void*) &socket_desc;
303
304         psz_network = "ipv6";
305
306        /* Create, Bind the socket, ... with the appropriate module  */
307
308         if( !( p_network = module_Need( p_intf, "network", psz_network ) ) )
309         {
310             msg_Warn( p_intf, "failed to open a connection (udp)" );
311             return;
312         }
313         module_Unneed( p_intf, p_network );
314
315         fdv6 = socket_desc.i_handle;
316     }
317
318
319     /* read SAP packets */
320     while( !p_intf->b_die )
321     {
322         int i_read;
323
324         //memset( buffer, 0, MAX_SAP_BUFFER + 1);
325
326         i_read = NetRead( p_intf, fd, fdv6, buffer, MAX_SAP_BUFFER );
327
328         if( i_read < 0 )
329         {
330             msg_Err( p_intf, "Cannot read in the socket" );
331         }
332         if( i_read == 0 )
333         {
334             continue;
335         }
336         buffer[i_read] = '\0';
337
338         packet_handle( p_intf, buffer, i_read );
339
340     }
341
342     /* Closing socket */
343     if( fd > 0)
344     {
345         if( close( fd ) )
346         {
347             msg_Warn( p_intf, "Ohoh, unable to close the socket" );
348         }
349     }
350     if( fdv6 > 0)
351     {
352         if( close( fdv6 ) )
353         {
354             msg_Warn( p_intf, "Ohoh, unable to close the socket" );
355         }
356     }
357 }
358
359 /********************************************************************
360  * Kill
361  *******************************************************************
362  * Kills the SAP interface.
363  ********************************************************************/
364 static int Kill( intf_thread_t *p_intf )
365 {
366
367     p_intf->b_die = VLC_TRUE;
368
369     return VLC_SUCCESS;
370 }
371
372 /*******************************************************************
373  * sess_toitem : changes a sess_descr_t into a hurd of
374  * playlist_item_t, which are enqueued.
375  *******************************************************************
376  * Note : does not support sessions that take place on consecutive
377  * port or adresses yet.
378  *******************************************************************/
379
380 static int sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
381 {
382     playlist_item_t * p_item;
383     char *psz_uri, *psz_proto;
384     char *psz_port;
385     char *psz_uri_default;
386     int i_multicast;
387     int i_count , i;
388     vlc_bool_t b_http = VLC_FALSE;
389     char *psz_http_path = NULL;
390     playlist_t *p_playlist;
391
392     psz_uri_default = NULL;
393     cfield_parse( p_sd->psz_connection, &psz_uri_default );
394
395     for( i_count = 0 ; i_count < p_sd->i_media ; i_count++ )
396     {
397         p_item = malloc( sizeof( playlist_item_t ) );
398         if( p_item == NULL )
399         {
400             msg_Err( p_intf, "Not enough memory for p_item in sesstoitem()" );
401             return 0;
402         }
403         p_item->psz_name = strdup( p_sd->psz_sessionname );
404         p_item->i_type = 0;
405         p_item->i_status = 0;
406         p_item->b_autodeletion = VLC_FALSE;
407         p_item->psz_uri = NULL;
408         p_item->ppsz_options = NULL;
409         p_item->i_options = 0;
410
411         psz_uri = NULL;
412
413         /* Build what we have to put in p_item->psz_uri, with the m and
414          *  c fields  */
415
416         if( !p_sd->pp_media[i_count] )
417         {
418             return 0;
419         }
420
421         mfield_parse( p_sd->pp_media[i_count]->psz_medianame,
422                         & psz_proto, & psz_port );
423
424         if( !psz_proto || !psz_port )
425         {
426             return 0;
427         }
428
429         if( p_sd->pp_media[i_count]->psz_mediaconnection )
430         {
431             cfield_parse( p_sd->pp_media[i_count]->psz_mediaconnection,
432                             & psz_uri );
433         }
434         else
435         {
436             psz_uri = psz_uri_default;
437         }
438
439         if( psz_uri == NULL )
440         {
441             return 0;
442         }
443
444         for( i = 0 ; i< p_sd->i_attributes ; i++ )
445         {
446             if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "type") &&
447                 strstr( p_sd->pp_attributes[i]->psz_value, "http") )
448             {
449                 b_http = VLC_TRUE;
450             }
451             if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "http-path"))
452             {
453                 psz_http_path = strdup(  p_sd->pp_attributes[i]->psz_value );
454
455             }
456         }
457
458
459         /* Filling p_item->psz_uri */
460         if( b_http == VLC_FALSE )
461         {
462             i_multicast = ismult( psz_uri );
463
464             p_item->psz_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
465                         strlen( psz_port ) + 5 +i_multicast );
466
467             if( p_item->psz_uri == NULL )
468             {
469                 msg_Err( p_intf, "Not enough memory");
470                 free( p_item );
471                 return 0;
472             }
473
474             if( i_multicast == 1)
475             {
476                 sprintf( p_item->psz_uri, "%s://@%s:%s", psz_proto,
477                                  psz_uri, psz_port );
478             }
479             else
480             {
481                 sprintf( p_item->psz_uri, "%s://%s:%s", psz_proto,
482                             psz_uri, psz_port );
483             }
484         }
485         else
486         {
487             if( psz_http_path == NULL )
488                 psz_http_path = strdup("/");
489
490             p_item->psz_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
491                         strlen( psz_port ) + 3 + strlen(psz_http_path) );
492
493             if( p_item->psz_uri == NULL )
494             {
495                 msg_Err( p_intf, "Not enough memory");
496                 free( p_item );
497                 return 0;
498             }
499
500             sprintf( p_item->psz_uri, "%s://%s:%s%s", psz_proto,
501                             psz_uri, psz_port,psz_http_path );
502
503         }
504             /* Enqueueing p_item in the playlist */
505
506         if( p_item )
507         {
508             p_item->i_group = p_intf->p_sys->i_group;
509             p_item->b_enabled = VLC_TRUE;
510             p_item->psz_author = NULL;
511             p_playlist = vlc_object_find( p_intf,
512             VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
513
514             playlist_AddItem ( p_playlist, p_item,
515             PLAYLIST_CHECK_INSERT, PLAYLIST_END);
516             vlc_object_release( p_playlist );
517         }
518
519         if( psz_http_path )
520             free(psz_http_path);
521     }
522
523     return 1;
524
525 }
526
527 /**********************************************************************
528  * cfield_parse
529  *********************************************************************
530  * put into *ppsz_uri, the the uri in the cfield, psz_cfield.
531  *********************************************************************/
532
533 static void cfield_parse( char *psz_cfield, char **ppsz_uri )
534 {
535
536     char *psz_pos;
537     if( psz_cfield )
538     {
539         psz_pos = psz_cfield;
540
541         while( *psz_pos != ' ' && *psz_pos !='\0' )
542         {
543             psz_pos++;
544         }
545         psz_pos++;
546         while( *psz_pos != ' ' && *psz_pos !='\0' )
547         {
548             psz_pos++;
549         }
550         psz_pos++;
551         *ppsz_uri = psz_pos;
552         while( *psz_pos != ' ' && *psz_pos !='/'
553                         && *psz_pos != '\0' )
554         {
555             psz_pos++;
556         }
557         *psz_pos = '\0';
558
559     }
560     else
561     {
562         ppsz_uri = NULL;
563     }
564
565     return;
566
567 }
568
569 /**********************************************************************
570  * mfield_parse
571  *********************************************************************
572  * put into *ppsz_proto, and *ppsz_port, the protocol and the port.
573  *********************************************************************/
574
575
576 static void mfield_parse( char *psz_mfield, char **ppsz_proto,
577                char **ppsz_port )
578 {
579     char *psz_pos;
580     if( psz_mfield )
581     {
582         psz_pos = psz_mfield;
583         while( *psz_pos != '\0' && *psz_pos != ' ' )
584         {
585             psz_pos++;
586         }
587         psz_pos++;
588         *ppsz_port = psz_pos;
589         while( *psz_pos != '\0' && *psz_pos && *psz_pos !=' ' && *psz_pos!='/' )
590         {
591             psz_pos++;
592         }
593         if( *psz_pos == '/' )  // FIXME does not support multi-port
594         {
595             *psz_pos = '\0';
596             psz_pos++;
597             while( *psz_pos != '\0' && *psz_pos !=' ' )
598             {
599             psz_pos++;
600             }
601         }
602         *psz_pos = '\0';
603         psz_pos++;
604         *ppsz_proto = psz_pos;
605         while( *psz_pos!='\0' && *psz_pos !=' ' &&
606                         *psz_pos!='/' )
607         {
608             *psz_pos = tolower( *psz_pos );
609             psz_pos++;
610         }
611         *psz_pos = '\0';
612     }
613     else
614     {
615         *ppsz_proto = NULL;
616         *ppsz_port = NULL;
617     }
618     return;
619 }
620
621 /***********************************************************************
622  * parse_sap : Takes care of the SAP headers
623  ***********************************************************************
624  * checks if the packet has the true headers ;
625  * returns the SAP header lenhth
626  ***********************************************************************/
627
628 static int parse_sap( char *p_packet )
629 {
630     // According to RFC 2974
631     int i_hlen = 4;                           // Minimum header length is 4
632     i_hlen += (p_packet[0] & 0x10) ? 16 : 4;  // Address type IPv6=16bytes
633     i_hlen +=  p_packet[1];                   // Authentification length
634
635     //Looks for the first '\0' byte after length
636     for(;p_packet[i_hlen]!='\0'; i_hlen++);
637
638     if( i_hlen > 50 ) /* Definitely too long...
639                          Maybe we have a fucked up packet without  \0 */
640     {   /* As a workaround, we search for "v=" */
641         i_hlen = 4;
642         for(;p_packet[i_hlen] != 'v' && p_packet[i_hlen+1] != '=' ; i_hlen++);
643         return i_hlen-1;
644     }
645
646     return(i_hlen);
647 }
648
649 /*************************************************************************
650  * packet_handle : handle the received packet and enques the
651  * the understated session
652  *************************************************************************/
653
654 static int packet_handle( intf_thread_t * p_intf, char *p_packet, int i_len )
655 {
656     sess_descr_t * p_sd;
657     int i_hlen;                             // Header length
658
659     i_hlen = parse_sap(p_packet);
660
661     if( (i_hlen > 0) && (i_hlen < i_len) )
662     {
663         p_sd = parse_sdp( p_intf, p_packet + i_hlen +1);
664         if(p_sd)
665         {
666             sess_toitem ( p_intf, p_sd );
667             free_sd ( p_sd );
668             return VLC_TRUE;
669         }
670     }
671
672     return VLC_FALSE; // Invalid Packet
673 }
674
675
676
677
678 /***********************************************************************
679  * parse_sdp : SDP parsing
680  * *********************************************************************
681  * Make a sess_descr_t with a psz
682  ***********************************************************************/
683
684 static sess_descr_t *  parse_sdp( intf_thread_t * p_intf, char *p_packet )
685 {
686     sess_descr_t *  sd;
687     char *psz_eof;
688
689     unsigned int i;
690     // According to RFC 2327, the first bytes should be exactly "v="
691     if((p_packet[0] != 'v') || (p_packet[1] != '='))
692     {
693         msg_Warn(p_intf, "Bad SDP packet");
694         return NULL;
695     }
696
697     if( ( sd = malloc( sizeof(sess_descr_t) ) ) == NULL )
698     {
699         msg_Err( p_intf, "Not enough memory for sd in parse_sdp()" );
700         return( NULL );
701     }
702
703     sd->pp_media = NULL;
704     sd->pp_attributes = NULL;
705     sd->psz_origin = NULL;
706     sd->psz_sessionname = NULL;
707     sd->psz_information = NULL;
708     sd->psz_uri = NULL;
709     sd->psz_emails = NULL;
710     sd->psz_phone = NULL;
711     sd->psz_time = NULL;
712     sd->psz_repeat = NULL;
713     sd->psz_attribute = NULL;
714     sd->psz_connection = NULL;
715
716
717     sd->i_media      = 0;
718     sd->i_attributes = 0;
719
720     while( *p_packet != '\0'  )
721     {
722 #define FIELD_COPY( p ) \
723         p = strndup( &p_packet[2], i_field_len );
724
725         char *psz_end;
726         int  i_field_len;
727
728         while( *p_packet == '\n' || *p_packet == ' ' || *p_packet == '\t' )
729         {
730             p_packet++;
731         }
732         if( *p_packet == '\0' )
733         {
734             break;
735         }
736
737         if( ( psz_end = strchr( p_packet, '\n' ) ) == NULL )
738         {
739             psz_end = p_packet + strlen( p_packet );
740         }
741         i_field_len = psz_end - &p_packet[2];
742
743         if( p_packet[1] == '=' && i_field_len > 0)
744         {
745             switch( *p_packet )
746             {
747                 case( 'v' ):
748                     FIELD_COPY( sd->psz_version );
749                     break;
750                 case ( 'o' ):
751                     FIELD_COPY( sd->psz_origin );
752                     break;
753                 case( 's' ):
754                     FIELD_COPY( sd->psz_sessionname );
755                     break;
756                 case( 'i' ):
757                     FIELD_COPY( sd->psz_information );
758                     break;
759                 case( 'u' ):
760                     FIELD_COPY( sd->psz_uri );
761                     break;
762                 case( 'e' ):
763                     FIELD_COPY( sd->psz_emails );
764                     break;
765                 case( 'p' ):
766                     FIELD_COPY( sd->psz_phone );
767                     break;
768                 case( 't' ):
769                     FIELD_COPY( sd->psz_time );
770                     break;
771                 case( 'r' ):
772                     FIELD_COPY( sd->psz_repeat );
773                     break;
774                 case( 'a' ):
775                     if( sd->pp_attributes )
776                     {
777                         sd->pp_attributes =
778                             realloc( sd->pp_attributes,
779                                     sizeof( attr_descr_t ) *
780                                     ( sd->i_attributes +1 ) );
781                     }
782                     else
783                     {
784                         sd->pp_attributes = malloc( sizeof( void * ) );
785                     }
786                     if( !sd->pp_attributes )
787                     {
788                         msg_Warn( p_intf, "Out of memory !" );
789                         return NULL;
790                     }
791                     sd->pp_attributes[sd->i_attributes] =
792                             malloc( sizeof( attr_descr_t ) );
793                     if( ! sd->pp_attributes[sd->i_attributes])
794                     {
795                         msg_Warn( p_intf, "Out of memory !" );
796                         return NULL;
797                     }
798
799                     p_packet += 2;
800                     psz_eof = strchr( p_packet, ':');
801                     if(psz_eof)
802                         *psz_eof = '\0';
803                     sd->pp_attributes[sd->i_attributes]->psz_field =
804                             strdup( p_packet );
805                     if( psz_eof + 1 )
806                     {
807                         sd->pp_attributes[sd->i_attributes]->psz_value =
808                             strdup( ++psz_eof );
809                     }
810                     else
811                     {
812                         if( sd->pp_attributes[sd->i_attributes]->psz_field )
813                             free( sd->pp_attributes[sd->i_attributes]
814                                               ->psz_field );
815                         break;
816                     }
817                     for( i=0 ; i<
818                       strlen(sd->pp_attributes[sd->i_attributes]->psz_value) ;
819                              i++ )
820                     {
821                         if(sd->pp_attributes[sd->i_attributes]->psz_value[i]
822                                         =='\n' )
823                           sd->pp_attributes[sd->i_attributes]->psz_value[i]                                            =0;
824                     }
825                     sd->i_attributes++;
826                     break;
827
828                 case( 'm' ):
829                     if( sd->pp_media )
830                     {
831                         sd->pp_media =
832                             realloc( sd->pp_media,
833                                sizeof( media_descr_t ) * ( sd->i_media + 1 ) );
834                     }
835                     else
836                     {
837                         sd->pp_media = malloc( sizeof( void * ) );
838                     }
839                     if( !sd->pp_media )
840                     {
841                         msg_Warn( p_intf, "Out of memory !" );
842                         return NULL;
843                     }
844                     sd->pp_media[sd->i_media] =
845                             malloc( sizeof( media_descr_t ) );
846                     sd->pp_media[sd->i_media]->psz_medianame = NULL;
847                     sd->pp_media[sd->i_media]->psz_mediaconnection = NULL;
848                     sd->pp_media[sd->i_media]->psz_medianame =
849                                    strndup( &p_packet[2], i_field_len );
850
851                     sd->i_media++;
852                     break;
853
854                 case( 'c' ):
855                     if( sd->i_media <= 0 )
856                     {
857                         FIELD_COPY(sd->psz_connection);
858                     }
859                     else
860                     {
861                         FIELD_COPY(sd->pp_media[sd->i_media - 1]->psz_mediaconnection);
862                     }
863                    break;
864                 default:
865                    break;
866             }
867         }
868         p_packet = psz_end;
869 #undef FIELD_COPY
870     }
871
872     return sd;
873 }
874
875 #define FREE( p ) \
876     if( p ) { free( p ); (p) = NULL; }
877 static void free_sd( sess_descr_t * p_sd )
878 {
879     int i;
880     if( p_sd )
881     {
882         FREE( p_sd->psz_origin );
883         FREE( p_sd->psz_sessionname );
884         FREE( p_sd->psz_information );
885         FREE( p_sd->psz_uri );
886         FREE( p_sd->psz_emails );
887         FREE( p_sd->psz_phone );
888         FREE( p_sd->psz_time );
889         FREE( p_sd->psz_repeat );
890         FREE( p_sd->psz_attribute );
891         FREE( p_sd->psz_connection );
892
893         for( i = 0; i < p_sd->i_media ; i++ )
894         {
895             FREE( p_sd->pp_media[i]->psz_medianame );
896             FREE( p_sd->pp_media[i]->psz_mediaconnection );
897         }
898         for( i = 0; i < p_sd->i_attributes ; i++ )
899         {
900             FREE( p_sd->pp_attributes[i]->psz_field );
901             FREE( p_sd->pp_attributes[i]->psz_value );
902         }
903         FREE( p_sd->pp_attributes );
904         FREE( p_sd->pp_media );
905
906         free( p_sd );
907     }
908     else
909     {
910         ;
911     }
912     return;
913 }
914
915 /***********************************************************************
916  * ismult: returns true if we have a multicast address
917  ***********************************************************************/
918
919 static int ismult( char *psz_uri )
920 {
921     char *psz_end;
922     int  i_value;
923
924     i_value = strtol( psz_uri, &psz_end, 0 );
925
926     /* IPv6 */
927     if( psz_uri[0] == '[')
928     {
929       if( strncasecmp( &psz_uri[1], "FF0" , 3) ||
930           strncasecmp( &psz_uri[2], "FF0" , 3))
931             return( VLC_TRUE );
932         else
933             return( VLC_FALSE );
934     }
935
936     if( *psz_end != '.' ) { return( VLC_FALSE ); }
937
938     return( i_value < 224 ? VLC_FALSE : VLC_TRUE );
939 }
940
941
942
943 /*****************************************************************************
944  * Read: read on a file descriptor, checking b_die periodically
945  *****************************************************************************
946  * Taken from udp.c
947  ******************************************************************************/
948 static ssize_t NetRead( intf_thread_t *p_intf,
949                         int i_handle, int i_handle_v6,
950                         byte_t *p_buffer, size_t i_len)
951 {
952 #ifdef UNDER_CE
953     return -1;
954
955 #else
956     struct timeval  timeout;
957     fd_set          fds;
958     int             i_ret;
959     int             i_max_handle;
960
961     ssize_t i_recv=-1;
962
963     /* Get the max handle for select */
964     if( i_handle_v6 > i_handle )
965         i_max_handle = i_handle_v6;
966     else
967         i_max_handle = i_handle;
968
969
970     /* Initialize file descriptor set */
971     FD_ZERO( &fds );
972     if(   i_handle > 0   ) FD_SET( i_handle, &fds );
973     if( i_handle_v6  > 0 ) FD_SET( i_handle_v6, &fds);
974
975
976     /* We'll wait 0.5 second if nothing happens */
977     timeout.tv_sec = 0;
978     timeout.tv_usec = 500000;
979
980     /* Find if some data is available */
981     i_ret = select( i_max_handle + 1, &fds,
982     NULL, NULL, &timeout );
983
984     if( i_ret == -1 && errno != EINTR )
985     {
986         msg_Err( p_intf, "network select error (%s)", strerror(errno) );
987     }
988     else if( i_ret > 0 )
989    {
990       /* Get the data */
991       if(i_handle >0)
992       {
993          if(FD_ISSET( i_handle, &fds ))
994          {
995              i_recv = recv( i_handle, p_buffer, i_len, 0 );
996          }
997       }
998       if(i_handle_v6 >0)
999       {
1000          if(FD_ISSET( i_handle_v6, &fds ))
1001          {
1002             i_recv = recv( i_handle_v6, p_buffer, i_len, 0 );
1003          }
1004       }
1005
1006        if( i_recv < 0 )
1007         {
1008            msg_Err( p_intf, "recv failed (%s)", strerror(errno) );
1009         }
1010         return i_recv;
1011     }
1012
1013     return 0;
1014
1015 #endif
1016 }
1017