]> git.sesse.net Git - vlc/blob - src/network/io.c
Whenever using select() companion macro FD_SET(), one must ensure that
[vlc] / src / network / io.c
1 /*****************************************************************************
2  * io.c: network I/O functions
3  *****************************************************************************
4  * Copyright (C) 2004-2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@videolan.org>
8  *          RĂ©mi Denis-Courmont <rem # videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>
29 #include <vlc/vlc.h>
30
31 #include <errno.h>
32
33 #ifdef HAVE_FCNTL_H
34 #   include <fcntl.h>
35 #endif
36 #ifdef HAVE_SYS_TIME_H
37 #    include <sys/time.h>
38 #endif
39 #ifdef HAVE_UNISTD_H
40 #   include <unistd.h>
41 #endif
42
43 #include "network.h"
44
45 #ifndef INADDR_ANY
46 #   define INADDR_ANY  0x00000000
47 #endif
48 #ifndef INADDR_NONE
49 #   define INADDR_NONE 0xFFFFFFFF
50 #endif
51
52 int net_Socket( vlc_object_t *p_this, int i_family, int i_socktype,
53                 int i_protocol )
54 {
55     int fd, i_val;
56
57     fd = socket( i_family, i_socktype, i_protocol );
58     if( fd == -1 )
59     {
60 #if defined(WIN32) || defined(UNDER_CE)
61         if( WSAGetLastError ( ) != WSAEAFNOSUPPORT )
62             msg_Warn( p_this, "cannot create socket (%i)",
63                       WSAGetLastError() );
64 #else
65         if( errno != EAFNOSUPPORT )
66             msg_Warn( p_this, "cannot create socket (%s)",
67                       strerror( errno ) );
68 #endif
69         return -1;
70     }
71
72     if( fd >= FD_SETSIZE )
73     {
74         msg_Err( p_this, "cannot create socket (too many already in use)" );
75         net_Close( fd );
76         return -1;
77     }
78
79     /* Set to non-blocking */
80 #if defined( WIN32 ) || defined( UNDER_CE )
81     {
82         unsigned long i_dummy = 1;
83         if( ioctlsocket( fd, FIONBIO, &i_dummy ) != 0 )
84             msg_Err( p_this, "cannot set socket to non-blocking mode" );
85     }
86 #else
87     fcntl( fd, F_SETFD, FD_CLOEXEC );
88
89     if( ( ( i_val = fcntl( fd, F_GETFL, 0 ) ) < 0 ) ||
90         ( fcntl( fd, F_SETFL, i_val | O_NONBLOCK ) < 0 ) )
91         msg_Err( p_this, "cannot set socket to non-blocking mode (%s)",
92                  strerror( errno ) );
93 #endif
94
95     i_val = 1;
96     setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (void *)&i_val,
97                 sizeof( i_val ) );
98
99 #ifdef IPV6_V6ONLY
100     /*
101      * Accepts only IPv6 connections on IPv6 sockets
102      * (and open an IPv4 socket later as well if needed).
103      * Only Linux and FreeBSD can map IPv4 connections on IPv6 sockets,
104      * so this allows for more uniform handling across platforms. Besides,
105      * it makes sure that IPv4 addresses will be printed as w.x.y.z rather
106      * than ::ffff:w.x.y.z
107      */
108     if( i_family == AF_INET6 )
109         setsockopt( fd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&i_val,
110                     sizeof( i_val ) );
111 #endif
112
113 #if defined( WIN32 ) || defined( UNDER_CE )
114 # ifndef IPV6_PROTECTION_LEVEL
115 #  define IPV6_PROTECTION_LEVEL 23
116 # endif
117     if( i_family == AF_INET6 )
118     {
119         i_val = 30 /*PROTECTION_LEVEL_UNRESTRICTED*/;
120         setsockopt( fd, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL,
121                    (const char*)&i_val, sizeof( i_val ) );
122     }
123 #endif
124     return fd;
125 }
126
127
128 /*****************************************************************************
129  * __net_Close:
130  *****************************************************************************
131  * Close a network handle
132  *****************************************************************************/
133 void net_Close( int fd )
134 {
135 #ifdef UNDER_CE
136     CloseHandle( (HANDLE)fd );
137 #elif defined( WIN32 )
138     closesocket( fd );
139 #else
140     close( fd );
141 #endif
142 }
143
144 /*****************************************************************************
145  * __net_Read:
146  *****************************************************************************
147  * Read from a network socket
148  * If b_retry is true, then we repeat until we have read the right amount of
149  * data
150  *****************************************************************************/
151 int __net_Read( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
152                 uint8_t *p_data, int i_data, vlc_bool_t b_retry )
153 {
154     struct timeval  timeout;
155     fd_set          fds_r, fds_e;
156     int             i_recv;
157     int             i_total = 0;
158     int             i_ret;
159     vlc_bool_t      b_die = p_this->b_die;
160
161     while( i_data > 0 )
162     {
163         do
164         {
165             if( p_this->b_die != b_die )
166             {
167                 return 0;
168             }
169
170             /* Initialize file descriptor set */
171             FD_ZERO( &fds_r );
172             FD_SET( fd, &fds_r );
173             FD_ZERO( &fds_e );
174             FD_SET( fd, &fds_e );
175
176             /* We'll wait 0.5 second if nothing happens */
177             timeout.tv_sec = 0;
178             timeout.tv_usec = 500000;
179
180         } while( (i_ret = select(fd + 1, &fds_r, NULL, &fds_e, &timeout)) == 0
181                  || ( i_ret < 0 && errno == EINTR ) );
182
183         if( i_ret < 0 )
184         {
185 #if defined(WIN32) || defined(UNDER_CE)
186             msg_Err( p_this, "network select error (%d)", WSAGetLastError() );
187 #else
188             msg_Err( p_this, "network select error (%s)", strerror(errno) );
189 #endif
190             return i_total > 0 ? i_total : -1;
191         }
192
193         if( ( i_recv = (p_vs != NULL)
194               ? p_vs->pf_recv( p_vs->p_sys, p_data, i_data )
195               : recv( fd, p_data, i_data, 0 ) ) < 0 )
196         {
197 #if defined(WIN32) || defined(UNDER_CE)
198             if( WSAGetLastError() == WSAEWOULDBLOCK )
199             {
200                 /* only happens with p_vs (SSL) - not really an error */
201             }
202             else
203             /* For udp only */
204             /* On win32 recv() will fail if the datagram doesn't fit inside
205              * the passed buffer, even though the buffer will be filled with
206              * the first part of the datagram. */
207             if( WSAGetLastError() == WSAEMSGSIZE )
208             {
209                 msg_Err( p_this, "recv() failed. "
210                          "Increase the mtu size (--mtu option)" );
211                 i_total += i_data;
212             }
213             else if( WSAGetLastError() == WSAEINTR ) continue;
214             else msg_Err( p_this, "recv failed (%i)", WSAGetLastError() );
215 #else
216             /* EAGAIN only happens with p_vs (TLS) and it's not an error */
217             if( errno != EAGAIN )
218                 msg_Err( p_this, "recv failed (%s)", strerror(errno) );
219 #endif
220             return i_total > 0 ? i_total : -1;
221         }
222         else if( i_recv == 0 )
223         {
224             /* Connection closed */
225             b_retry = VLC_FALSE;
226         }
227
228         p_data += i_recv;
229         i_data -= i_recv;
230         i_total+= i_recv;
231         if( !b_retry )
232         {
233             break;
234         }
235     }
236     return i_total;
237 }
238
239 /*****************************************************************************
240  * __net_ReadNonBlock:
241  *****************************************************************************
242  * Read from a network socket, non blocking mode (with timeout)
243  *****************************************************************************/
244 int __net_ReadNonBlock( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
245                         uint8_t *p_data, int i_data, mtime_t i_wait)
246 {
247     struct timeval  timeout;
248     fd_set          fds_r, fds_e;
249     int             i_recv;
250     int             i_ret;
251
252     /* Initialize file descriptor set */
253     FD_ZERO( &fds_r );
254     FD_SET( fd, &fds_r );
255     FD_ZERO( &fds_e );
256     FD_SET( fd, &fds_e );
257
258     timeout.tv_sec = 0;
259     timeout.tv_usec = i_wait;
260
261     i_ret = select(fd + 1, &fds_r, NULL, &fds_e, &timeout);
262
263     if( i_ret < 0 && errno == EINTR )
264     {
265         return 0;
266     }
267     else if( i_ret < 0 )
268     {
269 #if defined(WIN32) || defined(UNDER_CE)
270         msg_Err( p_this, "network select error (%d)", WSAGetLastError() );
271 #else
272         msg_Err( p_this, "network select error (%s)", strerror(errno) );
273 #endif
274         return -1;
275     }
276     else if( i_ret == 0)
277     {
278         return 0;
279     }
280     else
281     {
282 #if !defined(UNDER_CE)
283         if( fd == 0/*STDIN_FILENO*/ ) i_recv = read( fd, p_data, i_data ); else
284 #endif
285         if( ( i_recv = (p_vs != NULL)
286               ? p_vs->pf_recv( p_vs->p_sys, p_data, i_data )
287               : recv( fd, p_data, i_data, 0 ) ) < 0 )
288         {
289 #if defined(WIN32) || defined(UNDER_CE)
290             /* For udp only */
291             /* On win32 recv() will fail if the datagram doesn't fit inside
292              * the passed buffer, even though the buffer will be filled with
293              * the first part of the datagram. */
294             if( WSAGetLastError() == WSAEMSGSIZE )
295             {
296                 msg_Err( p_this, "recv() failed. "
297                          "Increase the mtu size (--mtu option)" );
298             }
299             else msg_Err( p_this, "recv failed (%i)", WSAGetLastError() );
300 #else
301             msg_Err( p_this, "recv failed (%s)", strerror(errno) );
302 #endif
303             return -1;
304         }
305
306         return i_recv ? i_recv : -1;  /* !i_recv -> connection closed if tcp */
307     }
308
309     /* We will never be here */
310     return -1;
311 }
312
313 /*****************************************************************************
314  * __net_Select:
315  *****************************************************************************
316  * Read from several sockets (with timeout). Takes data from the first socket
317  * that has some.
318  *****************************************************************************/
319 int __net_Select( vlc_object_t *p_this, int *pi_fd, v_socket_t **pp_vs,
320                   int i_fd, uint8_t *p_data, int i_data, mtime_t i_wait )
321 {
322     struct timeval  timeout;
323     fd_set          fds_r, fds_e;
324     int             i_recv;
325     int             i_ret;
326     int             i;
327     int             i_max_fd = 0;
328
329     /* Initialize file descriptor set */
330     FD_ZERO( &fds_r );
331     FD_ZERO( &fds_e );
332
333     for( i = 0 ; i < i_fd ; i++)
334     {
335         if( pi_fd[i] > i_max_fd ) i_max_fd = pi_fd[i];
336         FD_SET( pi_fd[i], &fds_r );
337         FD_SET( pi_fd[i], &fds_e );
338     }
339
340     timeout.tv_sec = 0;
341     timeout.tv_usec = i_wait;
342
343     i_ret = select( i_max_fd + 1, &fds_r, NULL, &fds_e, &timeout );
344
345     if( i_ret < 0 && errno == EINTR )
346     {
347         return 0;
348     }
349     else if( i_ret < 0 )
350     {
351         msg_Err( p_this, "network selection error (%s)", strerror(errno) );
352         return -1;
353     }
354     else if( i_ret == 0 )
355     {
356         return 0;
357     }
358     else
359     {
360         for( i = 0 ; i < i_fd ; i++)
361         {
362             if( FD_ISSET( pi_fd[i], &fds_r ) )
363             {
364                 i_recv = ((pp_vs != NULL) && (pp_vs[i] != NULL))
365                          ? pp_vs[i]->pf_recv( pp_vs[i]->p_sys, p_data, i_data )
366                          : recv( pi_fd[i], p_data, i_data, 0 );
367                 if( i_recv < 0 )
368                 {
369 #ifdef WIN32
370                     /* For udp only */
371                     /* On win32 recv() will fail if the datagram doesn't
372                      * fit inside the passed buffer, even though the buffer
373                      *  will be filled with the first part of the datagram. */
374                     if( WSAGetLastError() == WSAEMSGSIZE )
375                     {
376                         msg_Err( p_this, "recv() failed. "
377                              "Increase the mtu size (--mtu option)" );
378                     }
379                     else msg_Err( p_this, "recv failed (%i)",
380                                   WSAGetLastError() );
381 #else
382                     msg_Err( p_this, "recv failed (%s)", strerror(errno) );
383 #endif
384                     return VLC_EGENERIC;
385                 }
386
387                 return i_recv;
388             }
389         }
390     }
391
392     /* We will never be here */
393     return -1;
394 }
395
396
397 /* Write exact amount requested */
398 int __net_Write( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
399                  const uint8_t *p_data, int i_data )
400 {
401     struct timeval  timeout;
402     fd_set          fds_w, fds_e;
403     int             i_send;
404     int             i_total = 0;
405     int             i_ret;
406
407     vlc_bool_t      b_die = p_this->b_die;
408
409     while( i_data > 0 )
410     {
411         do
412         {
413             if( p_this->b_die != b_die )
414             {
415                 return 0;
416             }
417
418             /* Initialize file descriptor set */
419             FD_ZERO( &fds_w );
420             FD_SET( fd, &fds_w );
421             FD_ZERO( &fds_e );
422             FD_SET( fd, &fds_e );
423
424             /* We'll wait 0.5 second if nothing happens */
425             timeout.tv_sec = 0;
426             timeout.tv_usec = 500000;
427
428         } while( (i_ret = select(fd + 1, NULL, &fds_w, &fds_e, &timeout)) == 0
429                  || ( i_ret < 0 && errno == EINTR ) );
430
431         if( i_ret < 0 )
432         {
433 #if defined(WIN32) || defined(UNDER_CE)
434             msg_Err( p_this, "network selection error (%d)", WSAGetLastError() );
435 #else
436             msg_Err( p_this, "network selection error (%s)", strerror(errno) );
437 #endif
438             return i_total > 0 ? i_total : -1;
439         }
440
441         if( ( i_send = (p_vs != NULL)
442                        ? p_vs->pf_send( p_vs->p_sys, p_data, i_data )
443                        : send( fd, p_data, i_data, 0 ) ) < 0 )
444         {
445             /* XXX With udp for example, it will issue a message if the host
446              * isn't listening */
447             /* msg_Err( p_this, "send failed (%s)", strerror(errno) ); */
448             return i_total > 0 ? i_total : -1;
449         }
450
451         p_data += i_send;
452         i_data -= i_send;
453         i_total+= i_send;
454     }
455     return i_total;
456 }
457
458 char *__net_Gets( vlc_object_t *p_this, int fd, v_socket_t *p_vs )
459 {
460     char *psz_line = NULL, *ptr = NULL;
461     size_t  i_line = 0, i_max = 0;
462
463
464     for( ;; )
465     {
466         if( i_line == i_max )
467         {
468             i_max += 1024;
469             psz_line = realloc( psz_line, i_max );
470             ptr = psz_line + i_line;
471         }
472
473         if( net_Read( p_this, fd, p_vs, (uint8_t *)ptr, 1, VLC_TRUE ) != 1 )
474         {
475             if( i_line == 0 )
476             {
477                 free( psz_line );
478                 return NULL;
479             }
480             break;
481         }
482
483         if ( *ptr == '\n' )
484             break;
485
486         i_line++;
487         ptr++;
488     }
489
490     *ptr-- = '\0';
491
492     if( ( ptr >= psz_line ) && ( *ptr == '\r' ) )
493         *ptr = '\0';
494
495     return psz_line;
496 }
497
498 int net_Printf( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
499                 const char *psz_fmt, ... )
500 {
501     int i_ret;
502     va_list args;
503     va_start( args, psz_fmt );
504     i_ret = net_vaPrintf( p_this, fd, p_vs, psz_fmt, args );
505     va_end( args );
506
507     return i_ret;
508 }
509
510 int __net_vaPrintf( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
511                     const char *psz_fmt, va_list args )
512 {
513     char    *psz;
514     int     i_size, i_ret;
515
516     i_size = vasprintf( &psz, psz_fmt, args );
517     i_ret = __net_Write( p_this, fd, p_vs, (uint8_t *)psz, i_size ) < i_size
518         ? -1 : i_size;
519     free( psz );
520
521     return i_ret;
522 }
523
524
525 /*****************************************************************************
526  * inet_pton replacement for obsolete and/or crap operating systems
527  *****************************************************************************/
528 #ifndef HAVE_INET_PTON
529 int inet_pton(int af, const char *src, void *dst)
530 {
531 # ifdef WIN32
532     /* As we already know, Microsoft always go its own way, so even if they do
533      * provide IPv6, they don't provide the API. */
534     struct sockaddr_storage addr;
535     int len = sizeof( addr );
536
537     /* Damn it, they didn't even put LPCSTR for the firs parameter!!! */
538 #ifdef UNICODE
539     wchar_t *workaround_for_ill_designed_api =
540         malloc( MAX_PATH * sizeof(wchar_t) );
541     mbstowcs( workaround_for_ill_designed_api, src, MAX_PATH );
542     workaround_for_ill_designed_api[MAX_PATH-1] = 0;
543 #else
544     char *workaround_for_ill_designed_api = strdup( src );
545 #endif
546
547     if( !WSAStringToAddress( workaround_for_ill_designed_api, af, NULL,
548                              (LPSOCKADDR)&addr, &len ) )
549     {
550         free( workaround_for_ill_designed_api );
551         return -1;
552     }
553     free( workaround_for_ill_designed_api );
554
555     switch( af )
556     {
557         case AF_INET6:
558             memcpy( dst, &((struct sockaddr_in6 *)&addr)->sin6_addr, 16 );
559             break;
560
561         case AF_INET:
562             memcpy( dst, &((struct sockaddr_in *)&addr)->sin_addr, 4 );
563             break;
564
565         default:
566             WSASetLastError( WSAEAFNOSUPPORT );
567             return -1;
568     }
569 # else
570     /* Assume IPv6 is not supported. */
571     /* Would be safer and more simpler to use inet_aton() but it is most
572      * likely not provided either. */
573     uint32_t ipv4;
574
575     if( af != AF_INET )
576     {
577         errno = EAFNOSUPPORT;
578         return -1;
579     }
580
581     ipv4 = inet_addr( src );
582     if( ipv4 == INADDR_NONE )
583         return -1;
584
585     memcpy( dst, &ipv4, 4 );
586 # endif /* WIN32 */
587     return 0;
588 }
589 #endif /* HAVE_INET_PTON */