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