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