]> git.sesse.net Git - vlc/blob - modules/misc/sap.c
* modules/misc/freetype.c: compilation fix for windows
[vlc] / modules / misc / sap.c
1 /*****************************************************************************
2  * sap.c :  SAP interface module
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: sap.c,v 1.18 2003/07/05 15:00:28 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
92 static int  Activate     ( vlc_object_t * );
93 static void Run          ( intf_thread_t *p_intf );
94 static int Kill          ( intf_thread_t * );
95
96 static ssize_t NetRead    ( intf_thread_t*, int, int , byte_t *, size_t );
97
98 /* playlist related functions */
99 static int  sess_toitem( intf_thread_t *, sess_descr_t * );
100
101 /* sap/sdp related functions */
102 static int parse_sap ( char * );
103 static int packet_handle ( intf_thread_t *, char *, int );
104 static sess_descr_t *  parse_sdp( intf_thread_t *, char * ) ;
105
106 /* specific sdp fields parsing */
107
108 static void cfield_parse( char *, char ** );
109 static void mfield_parse( char *psz_mfield, char **ppsz_proto,
110                char **ppsz_port );
111
112 static void free_sd( sess_descr_t * );
113
114 /* Detect multicast addresses */
115 static int  ismult( char * );
116
117 /* The struct that contains sdp informations */
118 struct  sess_descr_t {
119     char *psz_version;
120     char *psz_origin;
121     char *psz_sessionname;
122     char *psz_information;
123     char *psz_uri;
124     char *psz_emails;
125     char *psz_phone;
126     char *psz_time;
127     char *psz_repeat;
128     char *psz_attribute;
129     char *psz_connection;
130     int  i_media;
131     media_descr_t ** pp_media;
132 };
133
134 /* All this informations are not useful yet.  */
135 struct media_descr_t {
136     char *psz_medianame;
137     char *psz_mediaconnection;
138 };
139
140 /*****************************************************************************
141  * Module descriptor
142  *****************************************************************************/
143
144 #define SAP_ADDR_TEXT N_("SAP multicast address")
145 #define SAP_ADDR_LONGTEXT N_("SAP multicast address")
146 #define SAP_IPV4_TEXT N_("No IPv4-SAP listening")
147 #define SAP_IPV4_LONGTEXT N_("Set this if you do not want SAP to listen for IPv4 announces")
148 #define SAP_IPV6_TEXT N_("IPv6-SAP listening")
149 #define SAP_IPV6_LONGTEXT N_("Set this if you want SAP to listen for IPv6 announces")
150 #define SAP_SCOPE_TEXT N_("IPv6 SAP scope")
151 #define SAP_SCOPE_LONGTEXT N_("Sets the scope for IPv6 announces (default is 8)")
152
153 vlc_module_begin();
154     add_category_hint( N_("SAP"), NULL, VLC_TRUE );
155         add_string( "sap-addr", NULL, NULL,
156                      SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, VLC_TRUE );
157     
158         add_bool( "no-sap-ipv4", 0 , NULL,
159                      SAP_IPV4_TEXT,SAP_IPV4_LONGTEXT, VLC_TRUE);
160
161         add_bool( "sap-ipv6", 0 , NULL,
162                    SAP_IPV6_TEXT, SAP_IPV6_LONGTEXT, VLC_TRUE);
163
164         add_string( "sap-ipv6-scope", "8" , NULL,
165                     SAP_SCOPE_TEXT, SAP_SCOPE_LONGTEXT, VLC_TRUE);
166
167     set_description( _("SAP interface") );
168     set_capability( "interface", 0 );
169     set_callbacks( Activate, NULL);
170 vlc_module_end();
171
172 /*****************************************************************************
173  * Activate: initialize and create stuff
174  *****************************************************************************/
175 static int Activate( vlc_object_t *p_this )
176 {
177     intf_thread_t *p_intf = (intf_thread_t*)p_this;
178
179     p_intf->pf_run = Run;
180
181     return VLC_SUCCESS;
182 }
183
184 /*****************************************************************************
185  * Run: sap thread
186  *****************************************************************************
187  * Listens to SAP packets, and sends them to packet_handle
188  *****************************************************************************/
189 #define MAX_SAP_BUFFER 2000
190
191 static void Run( intf_thread_t *p_intf )
192 {
193     char *psz_addr;
194     char *psz_addrv6;
195     char *psz_network = NULL;
196     int fd            = - 1;
197     int fdv6          = -1;
198     
199     int no_sap_ipv4      = config_GetInt( p_intf, "no-sap-ipv4" );
200     int sap_ipv6         = config_GetInt( p_intf, "sap-ipv6" );
201     char *sap_ipv6_scope = config_GetPsz( p_intf, "sap-ipv6-scope" );
202     
203     char buffer[MAX_SAP_BUFFER + 1];
204
205     module_t            *p_network;
206     network_socket_t    socket_desc;
207
208     if( no_sap_ipv4 == -1 || sap_ipv6 == -1 || sap_ipv6_scope == NULL )
209     {
210         msg_Warn( p_intf, "Unable to parse module configuration" );
211         return;
212     }
213     
214     /* Prepare IPv4 Networking */
215     if ( no_sap_ipv4 == 0)
216     {
217         if( !(psz_addr = config_GetPsz( p_intf, "sap-addr" ) ) )
218         { 
219             psz_addr = strdup( HELLO_GROUP );
220         } 
221             
222         /* Prepare the network_socket_t structure */
223         socket_desc.i_type            = NETWORK_UDP;
224         socket_desc.psz_bind_addr     = psz_addr;
225         socket_desc.i_bind_port       = HELLO_PORT;
226         socket_desc.psz_server_addr   = "";
227         socket_desc.i_server_port     = 0;
228         p_intf->p_private = (void*) &socket_desc;
229
230         psz_network = "ipv4"; 
231
232        /* Create, Bind the socket, ... with the appropriate module  */
233  
234         if( !( p_network = module_Need( p_intf, "network", psz_network ) ) )
235         {
236             msg_Warn( p_intf, "failed to open a connection (udp)" );
237             return;
238         }
239         module_Unneed( p_intf, p_network );
240
241         fd = socket_desc.i_handle;
242     }
243
244     /* Prepare IPv6 Networking */
245     if ( sap_ipv6 > 0)
246     {
247         /* Prepare the network_socket_t structure */
248
249         psz_addrv6=(char *)malloc(sizeof(char)*38); 
250         /* Max size of an IPv6 address */
251         
252         sprintf(psz_addrv6,"[%s%c%s]",IPV6_ADDR_1,
253                         sap_ipv6_scope[0],IPV6_ADDR_2);
254         
255         socket_desc.i_type            = NETWORK_UDP;
256         socket_desc.psz_bind_addr     = psz_addrv6;
257         socket_desc.i_bind_port       = HELLO_PORT;
258         socket_desc.psz_server_addr   = "";
259         socket_desc.i_server_port     = 0;
260         p_intf->p_private = (void*) &socket_desc;
261
262         psz_network = "ipv6"; 
263
264        /* Create, Bind the socket, ... with the appropriate module  */
265  
266         if( !( p_network = module_Need( p_intf, "network", psz_network ) ) )
267         {
268             msg_Warn( p_intf, "failed to open a connection (udp)" );
269             return;
270         }
271         module_Unneed( p_intf, p_network );
272
273         fdv6 = socket_desc.i_handle;
274     }
275
276     
277     /* read SAP packets */
278     while( !p_intf->b_die )
279     {
280         int i_read;
281
282         //memset( buffer, 0, MAX_SAP_BUFFER + 1);
283
284         i_read = NetRead( p_intf, fd, fdv6, buffer, MAX_SAP_BUFFER );
285
286         if( i_read < 0 )
287         {
288             msg_Err( p_intf, "Cannot read in the socket" );
289         }
290         if( i_read == 0 )
291         {
292             continue;
293         }
294         buffer[i_read] = '\0';
295
296         packet_handle( p_intf, buffer, i_read );
297
298     }
299
300     /* Closing socket */
301     close( socket_desc.i_handle );
302 }
303
304 /********************************************************************
305  * Kill
306  *******************************************************************
307  * Kills the SAP interface.
308  ********************************************************************/
309 static int Kill( intf_thread_t *p_intf )
310 {
311
312     p_intf->b_die = VLC_TRUE;
313
314     return VLC_SUCCESS;
315 }
316
317 /*******************************************************************
318  * sess_toitem : changes a sess_descr_t into a hurd of
319  * playlist_item_t, which are enqueued.
320  *******************************************************************
321  * Note : does not support sessions that take place on consecutive
322  * port or adresses yet.
323  *******************************************************************/
324
325 static int sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
326 {
327     playlist_item_t * p_item;
328     char *psz_uri, *psz_proto;
329     char *psz_port;
330     char *psz_uri_default;
331     int i_multicast;
332     int i_count;
333     playlist_t *p_playlist;
334
335     psz_uri_default = NULL;
336     cfield_parse( p_sd->psz_connection, &psz_uri_default );
337
338     for( i_count = 0 ; i_count < p_sd->i_media ; i_count++ )
339     {
340         p_item = malloc( sizeof( playlist_item_t ) );
341         if( p_item == NULL )
342         {
343             msg_Err( p_intf, "Not enough memory for p_item in sesstoitem()" );
344             return 0;
345         }
346         p_item->psz_name = strdup( p_sd->psz_sessionname );
347         p_item->i_type = 0;
348         p_item->i_status = 0;
349         p_item->b_autodeletion = VLC_FALSE;
350         p_item->psz_uri = NULL;
351
352         psz_uri = NULL;
353
354         /* Build what we have to put in p_item->psz_uri, with the m and
355          *  c fields  */
356
357         if( !p_sd->pp_media[i_count] )
358         {
359             return 0;
360         }
361
362         mfield_parse( p_sd->pp_media[i_count]->psz_medianame,
363                         & psz_proto, & psz_port );
364
365         if( !psz_proto || !psz_port )
366         {
367             return 0;
368         }
369
370         if( p_sd->pp_media[i_count]->psz_mediaconnection )
371         {
372             cfield_parse( p_sd->pp_media[i_count]->psz_mediaconnection,
373                             & psz_uri );
374         }
375         else
376         {
377             psz_uri = psz_uri_default;
378         }
379
380         if( psz_uri == NULL )
381         {
382             return 0;
383         }
384
385
386         /* Filling p_item->psz_uri */
387         i_multicast = ismult( psz_uri );
388
389         p_item->psz_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
390                         strlen( psz_port ) + 5 +i_multicast );
391         if( p_item->psz_uri == NULL )
392         {
393             msg_Err( p_intf, "Not enough memory");
394             free( p_item );
395             return 0;
396         }
397
398         if( i_multicast == 1)
399         {
400             sprintf( p_item->psz_uri, "%s://@%s:%s", psz_proto,
401                             psz_uri, psz_port );
402         }
403         else
404         {
405             sprintf( p_item->psz_uri, "%s://%s:%s", psz_proto,
406                             psz_uri, psz_port );
407         }
408
409         /* Enqueueing p_item in the playlist */
410
411         if( p_item )
412         {
413             p_playlist = vlc_object_find( p_intf,
414             VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
415
416             playlist_AddItem ( p_playlist, p_item,
417             PLAYLIST_CHECK_INSERT, PLAYLIST_END);
418             vlc_object_release( p_playlist );
419         }
420
421
422     }
423
424
425     return 1;
426
427 }
428
429 /**********************************************************************
430  * cfield_parse
431  *********************************************************************
432  * put into *ppsz_uri, the the uri in the cfield, psz_cfield.
433  *********************************************************************/
434
435 static void cfield_parse( char *psz_cfield, char **ppsz_uri )
436 {
437
438     char *psz_pos;
439     if( psz_cfield )
440     {
441         psz_pos = psz_cfield;
442
443         while( *psz_pos != ' ' && *psz_pos !='\0' )
444         {
445             psz_pos++;
446         }
447         psz_pos++;
448         while( *psz_pos != ' ' && *psz_pos !='\0' )
449         {
450             psz_pos++;
451         }
452         psz_pos++;
453         *ppsz_uri = psz_pos;
454         while( *psz_pos != ' ' && *psz_pos !='/'
455                         && *psz_pos != '\0' )
456         {
457             psz_pos++;
458         }
459         *psz_pos = '\0';
460
461     }
462     else
463     {
464         ppsz_uri = NULL;
465     }
466
467     return;
468
469 }
470
471 /**********************************************************************
472  * mfield_parse
473  *********************************************************************
474  * put into *ppsz_proto, and *ppsz_port, the protocol and the port.
475  *********************************************************************/
476
477
478 static void mfield_parse( char *psz_mfield, char **ppsz_proto,
479                char **ppsz_port )
480 {
481     char *psz_pos;
482     if( psz_mfield )
483     {
484         psz_pos = psz_mfield;
485         while( *psz_pos != '\0' && *psz_pos != ' ' )
486         {
487             psz_pos++;
488         }
489         psz_pos++;
490         *ppsz_port = psz_pos;
491         while( *psz_pos != '\0' && *psz_pos && *psz_pos !=' ' && *psz_pos!='/' )
492         {
493             psz_pos++;
494         }
495         if( *psz_pos == '/' )  // FIXME does not support multi-port
496         {
497             *psz_pos = '\0';
498             psz_pos++;
499             while( *psz_pos != '\0' && *psz_pos !=' ' )
500             {
501             psz_pos++;
502             }
503         }
504         *psz_pos = '\0';
505         psz_pos++;
506         *ppsz_proto = psz_pos;
507         while( *psz_pos!='\0' && *psz_pos !=' ' &&
508                         *psz_pos!='/' )
509         {
510             *psz_pos = tolower( *psz_pos );
511             psz_pos++;
512         }
513         *psz_pos = '\0';
514     }
515     else
516     {
517         *ppsz_proto = NULL;
518         *ppsz_port = NULL;
519     }
520     return;
521 }
522
523 /***********************************************************************
524  * parse_sap : Takes care of the SAP headers
525  ***********************************************************************
526  * checks if the packet has the true headers ;
527  * returns the SAP header lenhth
528  ***********************************************************************/
529
530 static int parse_sap( char *p_packet )
531 {
532     // According to RFC 2974
533     int i_hlen = 4;                           // Minimum header length is 4
534     i_hlen += (p_packet[0] & 0x10) ? 16 : 4;  // Address type IPv6=16bytes
535     i_hlen +=  p_packet[1];                   // Authentification length
536
537     //Looks for the first '\0' byte after length
538     for(;p_packet[i_hlen]!='\0'; i_hlen++);
539
540     return(i_hlen);
541 }
542
543 /*************************************************************************
544  * packet_handle : handle the received packet and enques the
545  * the understated session
546  *************************************************************************/
547
548 static int packet_handle( intf_thread_t * p_intf, char *p_packet, int i_len )
549 {
550     sess_descr_t * p_sd;
551     int i_hlen;                             // Header length
552
553     i_hlen = parse_sap(p_packet);
554
555     if( (i_hlen > 0) && (i_hlen < i_len) )
556     {
557         p_sd = parse_sdp( p_intf, p_packet + i_hlen +1);
558         if(p_sd)
559         {
560             sess_toitem ( p_intf, p_sd );
561             free_sd ( p_sd );
562             return VLC_TRUE;
563         }
564     }
565
566     return VLC_FALSE; // Invalid Packet
567 }
568
569
570
571
572 /***********************************************************************
573  * parse_sdp : SDP parsing
574  * *********************************************************************
575  * Make a sess_descr_t with a psz
576  ***********************************************************************/
577
578 static sess_descr_t *  parse_sdp( intf_thread_t * p_intf, char *p_packet )
579 {
580     sess_descr_t *  sd;
581
582     // According to RFC 2327, the first bytes should be exactly "v="
583     if((p_packet[0] != 'v') || (p_packet[1] != '='))
584     {
585         msg_Warn(p_intf, "Bad SDP packet");
586         return NULL;
587     }
588
589     if( ( sd = malloc( sizeof(sess_descr_t) ) ) == NULL )
590     {
591         msg_Err( p_intf, "Not enough memory for sd in parse_sdp()" );
592         return( NULL );
593     }
594
595     sd->pp_media = NULL;
596     sd->psz_origin = NULL;
597     sd->psz_sessionname = NULL;
598     sd->psz_information = NULL;
599     sd->psz_uri = NULL;
600     sd->psz_emails = NULL;
601     sd->psz_phone = NULL;
602     sd->psz_time = NULL;
603     sd->psz_repeat = NULL;
604     sd->psz_attribute = NULL;
605     sd->psz_connection = NULL;
606
607     sd->i_media = 0;
608
609     while( *p_packet != '\0'  )
610     {
611 #define FIELD_COPY( p ) \
612         p = strndup( &p_packet[2], i_field_len );
613
614         char *psz_end;
615         int  i_field_len;
616
617         while( *p_packet == '\n' || *p_packet == ' ' || *p_packet == '\t' )
618         {
619             p_packet++;
620         }
621         if( *p_packet == '\0' )
622         {
623             break;
624         }
625
626         if( ( psz_end = strchr( p_packet, '\n' ) ) == NULL )
627         {
628             psz_end = p_packet + strlen( p_packet );
629         }
630         i_field_len = psz_end - &p_packet[2];
631
632         if( p_packet[1] == '=' && i_field_len > 0)
633         {
634             switch( *p_packet )
635             {
636                 case( 'v' ):
637                     FIELD_COPY( sd->psz_version );
638                     break;
639                 case ( 'o' ):
640                     FIELD_COPY( sd->psz_origin );
641                     break;
642                 case( 's' ):
643                     FIELD_COPY( sd->psz_sessionname );
644                     break;
645                 case( 'i' ):
646                     FIELD_COPY( sd->psz_information );
647                     break;
648                 case( 'u' ):
649                     FIELD_COPY( sd->psz_uri );
650                     break;
651                 case( 'e' ):
652                     FIELD_COPY( sd->psz_emails );
653                     break;
654                 case( 'p' ):
655                     FIELD_COPY( sd->psz_phone );
656                     break;
657                 case( 't' ):
658                     FIELD_COPY( sd->psz_time );
659                     break;
660                 case( 'r' ):
661                     FIELD_COPY( sd->psz_repeat );
662                     break;
663                 case( 'a' ):
664                     FIELD_COPY( sd->psz_attribute );
665                     break;
666
667                 case( 'm' ):
668                     if( sd->pp_media )
669                     {
670                         sd->pp_media =
671                             realloc( sd->pp_media,
672                                sizeof( media_descr_t ) * ( sd->i_media + 1 ) );
673                     }
674                     else
675                     {
676                         sd->pp_media = malloc( sizeof( void * ) );
677                     }
678
679                     sd->pp_media[sd->i_media] =
680                             malloc( sizeof( media_descr_t ) );
681                     sd->pp_media[sd->i_media]->psz_medianame = NULL;
682                     sd->pp_media[sd->i_media]->psz_mediaconnection = NULL;
683                     sd->pp_media[sd->i_media]->psz_medianame = strndup( &p_packet[2], i_field_len );
684
685                     sd->i_media++;
686                     break;
687
688                 case( 'c' ):
689                     if( sd->i_media <= 0 )
690                     {
691                         FIELD_COPY(sd->psz_connection);
692                     }
693                     else
694                     {
695                         FIELD_COPY(sd->pp_media[sd->i_media - 1]->psz_mediaconnection);
696                     }
697                    break;
698                 default:
699                    break;
700             }
701         }
702         p_packet = psz_end;
703 #undef FIELD_COPY
704     }
705
706     return sd;
707 }
708
709 #define FREE( p ) \
710     if( p ) { free( p ); (p) = NULL; }
711 static void free_sd( sess_descr_t * p_sd )
712 {
713     int i;
714     if( p_sd )
715     {
716         FREE( p_sd->psz_origin );
717         FREE( p_sd->psz_sessionname );
718         FREE( p_sd->psz_information );
719         FREE( p_sd->psz_uri );
720         FREE( p_sd->psz_emails );
721         FREE( p_sd->psz_phone );
722         FREE( p_sd->psz_time );
723         FREE( p_sd->psz_repeat );
724         FREE( p_sd->psz_attribute );
725         FREE( p_sd->psz_connection );
726
727         for( i = 0; i < p_sd->i_media ; i++ )
728         {
729             FREE( p_sd->pp_media[i]->psz_medianame );
730             FREE( p_sd->pp_media[i]->psz_mediaconnection );
731         }
732         FREE( p_sd->pp_media );
733
734         free( p_sd );
735     }
736     else
737     {
738         ;
739     }
740     return;
741 }
742
743 /***********************************************************************
744  * ismult: returns true if we have a multicast address
745  ***********************************************************************/
746
747 static int ismult( char *psz_uri )
748 {
749     char *psz_end;
750     int  i_value;
751
752     i_value = strtol( psz_uri, &psz_end, 0 );
753
754     /* IPv6 */
755     if( psz_uri[0] == '[') 
756     {
757       if( strncasecmp( &psz_uri[1], "FF0" , 3) || 
758           strncasecmp( &psz_uri[2], "FF0" , 3))
759             return( VLC_TRUE );
760         else
761             return( VLC_FALSE ); 
762     } 
763
764     if( *psz_end != '.' ) { return( VLC_FALSE ); }
765
766     return( i_value < 224 ? VLC_FALSE : VLC_TRUE );
767 }
768
769
770
771 /*****************************************************************************
772  * Read: read on a file descriptor, checking b_die periodically
773  *****************************************************************************
774  * Taken from udp.c
775  ******************************************************************************/
776 static ssize_t NetRead( intf_thread_t *p_intf,
777                         int i_handle, int i_handle_v6, 
778                         byte_t *p_buffer, size_t i_len)
779 {
780 #ifdef UNDER_CE
781     return -1;
782
783 #else
784     struct timeval  timeout;
785     fd_set          fds;
786     int             i_ret;
787     int             i_max_handle;
788
789     ssize_t i_recv=-1;
790     
791     /* Get the max handle for select */
792     if( i_handle_v6 > i_handle )
793         i_max_handle = i_handle_v6;
794     else
795         i_max_handle = i_handle;
796
797     
798     /* Initialize file descriptor set */
799     FD_ZERO( &fds );
800     if(   i_handle > 0   ) FD_SET( i_handle, &fds );
801     if( i_handle_v6  > 0 ) FD_SET( i_handle_v6, &fds);
802
803
804     /* We'll wait 0.5 second if nothing happens */
805     timeout.tv_sec = 0;
806     timeout.tv_usec = 500000;
807
808     /* Find if some data is available */
809     i_ret = select( i_max_handle + 1, &fds,
810     NULL, NULL, &timeout );
811
812     if( i_ret == -1 && errno != EINTR )
813     {
814         msg_Err( p_intf, "network select error (%s)", strerror(errno) );
815     }
816     else if( i_ret > 0 )
817    {
818       /* Get the data */     
819       if(i_handle >0)
820       {
821          if(FD_ISSET( i_handle, &fds ))
822          {
823              i_recv = recv( i_handle, p_buffer, i_len, 0 );
824          }
825       }
826       if(i_handle_v6 >0)
827       {
828          if(FD_ISSET( i_handle_v6, &fds ))
829          {
830             i_recv = recv( i_handle_v6, p_buffer, i_len, 0 );
831          }
832       }
833       
834        if( i_recv < 0 )
835         {
836            msg_Err( p_intf, "recv failed (%s)", strerror(errno) );
837         }
838         return i_recv;
839     }
840
841     return 0;
842
843 #endif
844 }
845