]> git.sesse.net Git - vlc/blob - src/network/io.c
Experimental poll()-capable merge of net_Read, net_ReadNonBlock and net_Select
[vlc] / src / network / io.c
1 /*****************************************************************************
2  * io.c: network I/O functions
3  *****************************************************************************
4  * Copyright (C) 2004-2005 the VideoLAN team
5  * Copyright © 2005-2006 Rémi Denis-Courmont
6  * $Id$
7  *
8  * Authors: Laurent Aimar <fenrir@videolan.org>
9  *          Rémi Denis-Courmont <rem # 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <stdlib.h>
30 #include <vlc/vlc.h>
31
32 #include <errno.h>
33 #include <assert.h>
34
35 #ifdef HAVE_FCNTL_H
36 #   include <fcntl.h>
37 #endif
38 #ifdef HAVE_SYS_TIME_H
39 #    include <sys/time.h>
40 #endif
41 #ifdef HAVE_UNISTD_H
42 #   include <unistd.h>
43 #endif
44 #ifdef HAVE_POLL
45 #   include <sys/poll.h>
46 #endif
47
48 #include "network.h"
49
50 #ifndef INADDR_ANY
51 #   define INADDR_ANY  0x00000000
52 #endif
53 #ifndef INADDR_NONE
54 #   define INADDR_NONE 0xFFFFFFFF
55 #endif
56
57 int net_Socket( vlc_object_t *p_this, int i_family, int i_socktype,
58                 int i_protocol )
59 {
60     int fd, i_val;
61
62     fd = socket( i_family, i_socktype, i_protocol );
63     if( fd == -1 )
64     {
65 #if defined(WIN32) || defined(UNDER_CE)
66         if( WSAGetLastError ( ) != WSAEAFNOSUPPORT )
67 #else
68         if( errno != EAFNOSUPPORT )
69 #endif
70             msg_Warn( p_this, "cannot create socket: %s",
71                       net_strerror(net_errno) );
72         return -1;
73     }
74
75 #if defined( WIN32 ) || defined( UNDER_CE )
76     {
77         unsigned long i_dummy = 1;
78         if( ioctlsocket( fd, FIONBIO, &i_dummy ) != 0 )
79             msg_Err( p_this, "cannot set socket to non-blocking mode" );
80     }
81 #else
82     if( fd >= FD_SETSIZE )
83     {
84         /* We don't want to overflow select() fd_set */
85         msg_Err( p_this, "cannot create socket (too many already in use)" );
86         net_Close( fd );
87         return -1;
88     }
89
90     fcntl( fd, F_SETFD, FD_CLOEXEC );
91     i_val = fcntl( fd, F_GETFL, 0 );
92     fcntl( fd, F_SETFL, ((i_val != -1) ? i_val : 0) | O_NONBLOCK );
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 static int
146 net_ReadInner( vlc_object_t *restrict p_this, unsigned fdc, const int *fdv,
147                v_socket_t *const *restrict vsv,
148                uint8_t *restrict buf, size_t buflen,
149                int wait_ms, vlc_bool_t waitall )
150 {
151     int total = 0, n;
152
153     do
154     {
155         if (waitall && (buflen == 0))
156             return total; // output buffer full
157
158         int delay_ms;
159         if (wait_ms != -1)
160         {
161             delay_ms = (wait_ms > 500) ? 500 : wait_ms;
162             wait_ms -= delay_ms;
163         }
164         else
165             delay_ms = 500;
166
167 #ifdef HAVE_POLL
168         struct pollfd ufd[fdc];
169         memset (ufd, 0, sizeof (ufd));
170
171         for (unsigned i = 0; i < fdc; i++)
172         {
173             ufd[i].fd = fdv[i];
174             ufd[i].events = POLLIN;
175         }
176
177         if (p_this->b_die)
178             return total;
179
180         n = poll (ufd, fdc, (wait_ms == -1) ? -1 : delay_ms);
181         if (n == -1)
182             goto error;
183
184         assert ((unsigned)n <= fdc);
185
186         for (int i = 0; n > 0; i++)
187             if (ufd[i].revents)
188             {
189                 fdc = 1;
190                 fdv += i;
191                 vsv += i;
192                 n--;
193                 goto receive;
194             }
195 #else
196         int maxfd = -1;
197         fd_set set;
198         FD_ZERO (&set);
199
200         for (unsigned i = 0; i < fdc; i++)
201         {
202 #if !defined(WIN32) && !defined(UNDER_CE)
203             if (fdv[i] >= FD_SETSIZE)
204             {
205                 /* We don't want to overflow select() fd_set */
206                 msg_Err( p_this, "select set overflow" );
207                 return -1;
208             }
209 #endif
210             FD_SET (fdv[i], &set);
211             if (fdv[i] > maxfd)
212                 maxfd = fdv[i];
213         }
214
215         n = select (maxfd + 1, &set, NULL, NULL,
216                     (wait_ms == -1) ? NULL
217                         : &(struct timeval){ 0, delay_ms * 1000 });
218         if (n == -1)
219             goto error;
220
221         for (unsigned i = 0; n > 0; i++)
222             if (FD_ISSET (fdv[i], &set))
223             {
224                 fdc = 1;
225                 fdv += i;
226                 vsv += i;
227                 n--;
228                 goto receive;
229             }
230 #endif
231
232         continue;
233
234 receive:
235         if ((*vsv) != NULL)
236             n = (*vsv)->pf_recv ((*vsv)->p_sys, buf, buflen);
237         else
238             n = recv (*fdv, buf, buflen, 0);
239
240         if (n == -1)
241         {
242 #if defined(WIN32) || defined(UNDER_CE)
243             switch (WSAGetLastError())
244             {
245                 case WSAEWOULDBLOCK:
246                 /* only happens with vs != NULL (SSL) - not really an error */
247                     continue;
248
249                 case WSAEMSGSIZE:
250                 /* For UDP only */
251                 /* On Win32, recv() fails if the datagram doesn't fit inside
252                  * the passed buffer, even though the buffer will be filled
253                  * with the first part of the datagram. */
254                     msg_Err( p_this, "recv() failed. "
255                                      "Increase the mtu size (--mtu option)" );
256                     total += buflen;
257                     return total;
258             }
259 #else
260             if( errno == EAGAIN ) /* spurious wake-up (sucks if fdc > 1) */
261                 continue;
262 #endif
263             goto error;
264         }
265
266         total += n;
267         buf += n;
268         buflen -= n;
269     }
270     while (wait_ms);
271
272     return total; // timeout
273
274 error:
275     msg_Err (p_this, "Receive error: %s", net_strerror (net_errno));
276     return (total > 0) ? total : -1;
277 }
278
279
280 /*****************************************************************************
281  * __net_Read:
282  *****************************************************************************
283  * Read from a network socket
284  * If b_retry is true, then we repeat until we have read the right amount of
285  * data
286  *****************************************************************************/
287 int __net_Read( vlc_object_t *restrict p_this, int fd,
288                 v_socket_t *restrict p_vs,
289                 uint8_t *restrict p_data, int i_data, vlc_bool_t b_retry )
290 {
291     struct timeval  timeout;
292     fd_set          fds_r, fds_e;
293     int             i_recv;
294     int             i_total = 0;
295     int             i_ret;
296     vlc_bool_t      b_die = p_this->b_die;
297
298     while( i_data > 0 )
299     {
300         do
301         {
302             if( p_this->b_die != b_die )
303             {
304                 return 0;
305             }
306
307             /* Initialize file descriptor set */
308             FD_ZERO( &fds_r );
309             FD_SET( fd, &fds_r );
310             FD_ZERO( &fds_e );
311             FD_SET( fd, &fds_e );
312
313             /* We'll wait 0.5 second if nothing happens */
314             timeout.tv_sec = 0;
315             timeout.tv_usec = 500000;
316
317         } while( (i_ret = select(fd + 1, &fds_r, NULL, &fds_e, &timeout)) == 0
318                  || ( i_ret < 0 && errno == EINTR ) );
319
320         if( i_ret < 0 )
321         {
322             msg_Err( p_this, "network select error (%s)",
323                      net_strerror(net_errno) );
324             return i_total > 0 ? i_total : -1;
325         }
326
327         if( ( i_recv = (p_vs != NULL)
328               ? p_vs->pf_recv( p_vs->p_sys, p_data, i_data )
329               : recv( fd, p_data, i_data, 0 ) ) < 0 )
330         {
331 #if defined(WIN32) || defined(UNDER_CE)
332             if( WSAGetLastError() == WSAEWOULDBLOCK )
333             {
334                 /* only happens with p_vs (SSL) - not really an error */
335             }
336             else
337             /* For udp only */
338             /* On win32 recv() will fail if the datagram doesn't fit inside
339              * the passed buffer, even though the buffer will be filled with
340              * the first part of the datagram. */
341             if( WSAGetLastError() == WSAEMSGSIZE )
342             {
343                 msg_Err( p_this, "recv() failed. "
344                          "Increase the mtu size (--mtu option)" );
345                 i_total += i_data;
346             }
347             else if( WSAGetLastError() == WSAEINTR ) continue;
348 #else
349             /* EAGAIN only happens with p_vs (TLS) and it's not an error */
350             if( errno != EAGAIN )
351 #endif
352                 msg_Err( p_this, "recv failed: %s", net_strerror(net_errno) );
353             return i_total > 0 ? i_total : -1;
354         }
355         else if( i_recv == 0 )
356         {
357             /* Connection closed */
358             b_retry = VLC_FALSE;
359         }
360
361         p_data += i_recv;
362         i_data -= i_recv;
363         i_total+= i_recv;
364         if( !b_retry )
365         {
366             break;
367         }
368     }
369     return i_total;
370 }
371
372 /*****************************************************************************
373  * __net_ReadNonBlock:
374  *****************************************************************************
375  * Read from a network socket, non blocking mode (with timeout)
376  *****************************************************************************/
377 int __net_ReadNonBlock( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
378                         uint8_t *p_data, int i_data, mtime_t i_wait)
379 {
380     struct timeval  timeout;
381     fd_set          fds_r, fds_e;
382     int             i_recv;
383     int             i_ret;
384
385     /* Initialize file descriptor set */
386     FD_ZERO( &fds_r );
387     FD_SET( fd, &fds_r );
388     FD_ZERO( &fds_e );
389     FD_SET( fd, &fds_e );
390
391     timeout.tv_sec = 0;
392     timeout.tv_usec = i_wait;
393
394     i_ret = select(fd + 1, &fds_r, NULL, &fds_e, &timeout);
395
396     if( i_ret < 0 && errno == EINTR )
397     {
398         return 0;
399     }
400     else if( i_ret < 0 )
401     {
402         msg_Err( p_this, "network error: %s", net_strerror(net_errno) );
403         return -1;
404     }
405     else if( i_ret == 0)
406     {
407         return 0;
408     }
409     else
410     {
411 #if !defined(UNDER_CE)
412         if( fd == 0/*STDIN_FILENO*/ ) i_recv = read( fd, p_data, i_data ); else
413 #endif
414         if( ( i_recv = (p_vs != NULL)
415               ? p_vs->pf_recv( p_vs->p_sys, p_data, i_data )
416               : recv( fd, p_data, i_data, 0 ) ) < 0 )
417         {
418 #if defined(WIN32) || defined(UNDER_CE)
419             /* For udp only */
420             /* On win32 recv() will fail if the datagram doesn't fit inside
421              * the passed buffer, even though the buffer will be filled with
422              * the first part of the datagram. */
423             if( WSAGetLastError() == WSAEMSGSIZE )
424             {
425                 msg_Err( p_this, "recv() failed. "
426                          "Increase the mtu size (--mtu option)" );
427             }
428             else
429 #endif
430             msg_Err( p_this, "recv failed: %s", net_strerror(net_errno) );
431             return -1;
432         }
433
434         return i_recv ? i_recv : -1;  /* !i_recv -> connection closed if tcp */
435     }
436
437     /* We will never be here */
438     return -1;
439 }
440
441 /*****************************************************************************
442  * __net_Select:
443  *****************************************************************************
444  * Read from several sockets (with timeout). Takes data from the first socket
445  * that has some.
446  *****************************************************************************/
447 int __net_Select( vlc_object_t *p_this, int *pi_fd, v_socket_t **pp_vs,
448                   int i_fd, uint8_t *p_data, int i_data, mtime_t i_wait )
449 {
450     struct timeval  timeout;
451     fd_set          fds_r, fds_e;
452     int             i_recv;
453     int             i_ret;
454     int             i;
455     int             i_max_fd = 0;
456
457     /* Initialize file descriptor set */
458     FD_ZERO( &fds_r );
459     FD_ZERO( &fds_e );
460
461     for( i = 0 ; i < i_fd ; i++)
462     {
463         if( pi_fd[i] > i_max_fd ) i_max_fd = pi_fd[i];
464         FD_SET( pi_fd[i], &fds_r );
465         FD_SET( pi_fd[i], &fds_e );
466     }
467
468     timeout.tv_sec = 0;
469     timeout.tv_usec = i_wait;
470
471     i_ret = select( i_max_fd + 1, &fds_r, NULL, &fds_e, &timeout );
472
473     if( i_ret < 0 && errno == EINTR )
474     {
475         return 0;
476     }
477     else if( i_ret < 0 )
478     {
479         msg_Err( p_this, "network selection error: %s", net_strerror(net_errno) );
480         return -1;
481     }
482     else if( i_ret == 0 )
483     {
484         return 0;
485     }
486     else
487     {
488         for( i = 0 ; i < i_fd ; i++)
489         {
490             if( FD_ISSET( pi_fd[i], &fds_r ) )
491             {
492                 i_recv = ((pp_vs != NULL) && (pp_vs[i] != NULL))
493                          ? pp_vs[i]->pf_recv( pp_vs[i]->p_sys, p_data, i_data )
494                          : recv( pi_fd[i], p_data, i_data, 0 );
495                 if( i_recv < 0 )
496                 {
497 #ifdef WIN32
498                     /* For udp only */
499                     /* On win32 recv() will fail if the datagram doesn't
500                      * fit inside the passed buffer, even though the buffer
501                      *  will be filled with the first part of the datagram. */
502                     if( WSAGetLastError() == WSAEMSGSIZE )
503                     {
504                         msg_Err( p_this, "recv() failed. "
505                              "Increase the mtu size (--mtu option)" );
506                     }
507                     else msg_Err( p_this, "recv failed (%i)",
508                                   WSAGetLastError() );
509 #else
510                     msg_Err( p_this, "recv failed (%s)", strerror(errno) );
511 #endif
512                     return VLC_EGENERIC;
513                 }
514
515                 return i_recv;
516             }
517         }
518     }
519
520     /* We will never be here */
521     return -1;
522 }
523
524
525 /* Write exact amount requested */
526 int __net_Write( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
527                  const uint8_t *p_data, int i_data )
528 {
529     struct timeval  timeout;
530     fd_set          fds_w, fds_e;
531     int             i_send;
532     int             i_total = 0;
533     int             i_ret;
534
535     vlc_bool_t      b_die = p_this->b_die;
536
537     while( i_data > 0 )
538     {
539         do
540         {
541             if( p_this->b_die != b_die )
542             {
543                 return 0;
544             }
545
546             /* Initialize file descriptor set */
547             FD_ZERO( &fds_w );
548             FD_SET( fd, &fds_w );
549             FD_ZERO( &fds_e );
550             FD_SET( fd, &fds_e );
551
552             /* We'll wait 0.5 second if nothing happens */
553             timeout.tv_sec = 0;
554             timeout.tv_usec = 500000;
555
556         } while( (i_ret = select(fd + 1, NULL, &fds_w, &fds_e, &timeout)) == 0
557                  || ( i_ret < 0 && errno == EINTR ) );
558
559         if( i_ret < 0 )
560         {
561 #if defined(WIN32) || defined(UNDER_CE)
562             msg_Err( p_this, "network selection error (%d)", WSAGetLastError() );
563 #else
564             msg_Err( p_this, "network selection error (%s)", strerror(errno) );
565 #endif
566             return i_total > 0 ? i_total : -1;
567         }
568
569         if( ( i_send = (p_vs != NULL)
570                        ? p_vs->pf_send( p_vs->p_sys, p_data, i_data )
571                        : send( fd, p_data, i_data, 0 ) ) < 0 )
572         {
573             /* XXX With udp for example, it will issue a message if the host
574              * isn't listening */
575             /* msg_Err( p_this, "send failed (%s)", strerror(errno) ); */
576             return i_total > 0 ? i_total : -1;
577         }
578
579         p_data += i_send;
580         i_data -= i_send;
581         i_total+= i_send;
582     }
583     return i_total;
584 }
585
586 char *__net_Gets( vlc_object_t *p_this, int fd, v_socket_t *p_vs )
587 {
588     char *psz_line = NULL, *ptr = NULL;
589     size_t  i_line = 0, i_max = 0;
590
591
592     for( ;; )
593     {
594         if( i_line == i_max )
595         {
596             i_max += 1024;
597             psz_line = realloc( psz_line, i_max );
598             ptr = psz_line + i_line;
599         }
600
601         if( net_Read( p_this, fd, p_vs, (uint8_t *)ptr, 1, VLC_TRUE ) != 1 )
602         {
603             if( i_line == 0 )
604             {
605                 free( psz_line );
606                 return NULL;
607             }
608             break;
609         }
610
611         if ( *ptr == '\n' )
612             break;
613
614         i_line++;
615         ptr++;
616     }
617
618     *ptr-- = '\0';
619
620     if( ( ptr >= psz_line ) && ( *ptr == '\r' ) )
621         *ptr = '\0';
622
623     return psz_line;
624 }
625
626 int net_Printf( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
627                 const char *psz_fmt, ... )
628 {
629     int i_ret;
630     va_list args;
631     va_start( args, psz_fmt );
632     i_ret = net_vaPrintf( p_this, fd, p_vs, psz_fmt, args );
633     va_end( args );
634
635     return i_ret;
636 }
637
638 int __net_vaPrintf( vlc_object_t *p_this, int fd, v_socket_t *p_vs,
639                     const char *psz_fmt, va_list args )
640 {
641     char    *psz;
642     int     i_size, i_ret;
643
644     i_size = vasprintf( &psz, psz_fmt, args );
645     i_ret = __net_Write( p_this, fd, p_vs, (uint8_t *)psz, i_size ) < i_size
646         ? -1 : i_size;
647     free( psz );
648
649     return i_ret;
650 }
651
652
653 /*****************************************************************************
654  * inet_pton replacement for obsolete and/or crap operating systems
655  *****************************************************************************/
656 #ifndef HAVE_INET_PTON
657 int inet_pton(int af, const char *src, void *dst)
658 {
659 # ifdef WIN32
660     /* As we already know, Microsoft always go its own way, so even if they do
661      * provide IPv6, they don't provide the API. */
662     struct sockaddr_storage addr;
663     int len = sizeof( addr );
664
665     /* Damn it, they didn't even put LPCSTR for the firs parameter!!! */
666 #ifdef UNICODE
667     wchar_t *workaround_for_ill_designed_api =
668         malloc( MAX_PATH * sizeof(wchar_t) );
669     mbstowcs( workaround_for_ill_designed_api, src, MAX_PATH );
670     workaround_for_ill_designed_api[MAX_PATH-1] = 0;
671 #else
672     char *workaround_for_ill_designed_api = strdup( src );
673 #endif
674
675     if( !WSAStringToAddress( workaround_for_ill_designed_api, af, NULL,
676                              (LPSOCKADDR)&addr, &len ) )
677     {
678         free( workaround_for_ill_designed_api );
679         return -1;
680     }
681     free( workaround_for_ill_designed_api );
682
683     switch( af )
684     {
685         case AF_INET6:
686             memcpy( dst, &((struct sockaddr_in6 *)&addr)->sin6_addr, 16 );
687             break;
688
689         case AF_INET:
690             memcpy( dst, &((struct sockaddr_in *)&addr)->sin_addr, 4 );
691             break;
692
693         default:
694             WSASetLastError( WSAEAFNOSUPPORT );
695             return -1;
696     }
697 # else
698     /* Assume IPv6 is not supported. */
699     /* Would be safer and more simpler to use inet_aton() but it is most
700      * likely not provided either. */
701     uint32_t ipv4;
702
703     if( af != AF_INET )
704     {
705         errno = EAFNOSUPPORT;
706         return -1;
707     }
708
709     ipv4 = inet_addr( src );
710     if( ipv4 == INADDR_NONE )
711         return -1;
712
713     memcpy( dst, &ipv4, 4 );
714 # endif /* WIN32 */
715     return 0;
716 }
717 #endif /* HAVE_INET_PTON */