]> git.sesse.net Git - vlc/blobdiff - src/network/io.c
httpd: cosmetics
[vlc] / src / network / io.c
index c4238d58bf7d3b80edef66ef31a0bcdf5bf0fa65..d8832e21f0b503a7bada50e75978c0a6d04899ef 100644 (file)
@@ -1,7 +1,7 @@
 /*****************************************************************************
  * io.c: network I/O functions
  *****************************************************************************
- * Copyright (C) 2004-2005, 2007 the VideoLAN team
+ * Copyright (C) 2004-2005, 2007 VLC authors and VideoLAN
  * Copyright © 2005-2006 Rémi Denis-Courmont
  * $Id$
  *
@@ -9,19 +9,19 @@
  *          Rémi Denis-Courmont <rem # videolan.org>
  *          Christophe Mutricy <xtophe at videolan dot org>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
 /*****************************************************************************
 #include <errno.h>
 #include <assert.h>
 
-#ifdef HAVE_FCNTL_H
-#   include <fcntl.h>
-#endif
-#ifdef HAVE_UNISTD_H
-#   include <unistd.h>
-#endif
+#include <fcntl.h>
+#include <unistd.h>
 #ifdef HAVE_POLL
 #   include <poll.h>
 #endif
 #   define INADDR_NONE 0xFFFFFFFF
 #endif
 
-#if defined(WIN32) || defined(UNDER_CE)
+#if defined(_WIN32)
 # undef EAFNOSUPPORT
 # define EAFNOSUPPORT WSAEAFNOSUPPORT
+# undef EWOULDBLOCK
+# define EWOULDBLOCK WSAEWOULDBLOCK
+# undef EAGAIN
+# define EAGAIN WSAEWOULDBLOCK
 #endif
 
 #ifdef HAVE_LINUX_DCCP_H
@@ -83,7 +83,8 @@ int net_Socket (vlc_object_t *p_this, int family, int socktype,
     if (fd == -1)
     {
         if (net_errno != EAFNOSUPPORT)
-            msg_Err (p_this, "cannot create socket: %m");
+            msg_Err (p_this, "cannot create socket: %s",
+                     vlc_strerror_c(net_errno));
         return -1;
     }
 
@@ -98,7 +99,7 @@ int net_Socket (vlc_object_t *p_this, int family, int socktype,
         setsockopt (fd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){ 1 }, sizeof (int));
 #endif
 
-#if defined (WIN32) || defined (UNDER_CE)
+#if defined (_WIN32)
 # ifndef IPV6_PROTECTION_LEVEL
 #  warning Please update your C library headers.
 #  define IPV6_PROTECTION_LEVEL 23
@@ -112,7 +113,7 @@ int net_Socket (vlc_object_t *p_this, int family, int socktype,
 #ifdef DCCP_SOCKOPT_SERVICE
     if (socktype == SOL_DCCP)
     {
-        char *dccps = var_CreateGetNonEmptyString (p_this, "dccp-service");
+        char *dccps = var_InheritString (p_this, "dccp-service");
         if (dccps != NULL)
         {
             setsockopt (fd, SOL_DCCP, DCCP_SOCKOPT_SERVICE, dccps,
@@ -127,20 +128,22 @@ int net_Socket (vlc_object_t *p_this, int family, int socktype,
 
 
 int *net_Listen (vlc_object_t *p_this, const char *psz_host,
-                 int i_port, int protocol)
+                 int i_port, int type, int protocol)
 {
-    struct addrinfo hints, *res;
-
-    memset (&hints, 0, sizeof( hints ));
-    hints.ai_protocol = protocol;
-    hints.ai_flags = AI_PASSIVE;
+    struct addrinfo hints = {
+        .ai_socktype = type,
+        .ai_protocol = protocol,
+        .ai_flags = AI_PASSIVE | AI_NUMERICSERV | AI_IDN,
+    }, *res;
 
-    msg_Dbg (p_this, "net: listening to %s port %d", psz_host, i_port);
+    msg_Dbg (p_this, "net: listening to %s port %d",
+             (psz_host != NULL) ? psz_host : "*", i_port);
 
-    int i_val = vlc_getaddrinfo (p_this, psz_host, i_port, &hints, &res);
+    int i_val = vlc_getaddrinfo (psz_host, i_port, &hints, &res);
     if (i_val)
     {
-        msg_Err (p_this, "Cannot resolve %s port %d : %s", psz_host, i_port,
+        msg_Err (p_this, "Cannot resolve %s port %d : %s",
+                 (psz_host != NULL) ? psz_host : "", i_port,
                  gai_strerror (i_val));
         return NULL;
     }
@@ -154,12 +157,12 @@ int *net_Listen (vlc_object_t *p_this, const char *psz_host,
                              ptr->ai_protocol);
         if (fd == -1)
         {
-            msg_Dbg (p_this, "socket error: %m");
+            msg_Dbg (p_this, "socket error: %s", vlc_strerror_c(net_errno));
             continue;
         }
 
         /* Bind the socket */
-#if defined (WIN32) || defined (UNDER_CE)
+#if defined (_WIN32)
         /*
          * Under Win32 and for multicasting, we bind to INADDR_ANY.
          * This is of course a severe bug, since the socket would logically
@@ -183,7 +186,7 @@ int *net_Listen (vlc_object_t *p_this, const char *psz_host,
         if (bind (fd, ptr->ai_addr, ptr->ai_addrlen))
         {
             net_Close (fd);
-#if !defined(WIN32) && !defined(UNDER_CE)
+#if !defined(_WIN32)
             fd = rootwrap_bind (ptr->ai_family, ptr->ai_socktype,
                                 ptr->ai_protocol,
                                 ptr->ai_addr, ptr->ai_addrlen);
@@ -194,7 +197,8 @@ int *net_Listen (vlc_object_t *p_this, const char *psz_host,
             else
 #endif
             {
-                msg_Err (p_this, "socket bind error (%m)");
+                msg_Err (p_this, "socket bind error: %s",
+                         vlc_strerror_c(net_errno));
                 continue;
             }
         }
@@ -219,7 +223,8 @@ int *net_Listen (vlc_object_t *p_this, const char *psz_host,
 #endif
                 if (listen (fd, INT_MAX))
                 {
-                    msg_Err (p_this, "socket listen error (%m)");
+                    msg_Err (p_this, "socket listen error: %s",
+                             vlc_strerror_c(net_errno));
                     net_Close (fd);
                     continue;
                 }
@@ -256,56 +261,27 @@ ssize_t
 net_Read (vlc_object_t *restrict p_this, int fd, const v_socket_t *vs,
           void *restrict p_buf, size_t i_buflen, bool waitall)
 {
-    size_t i_total = 0;
-    struct pollfd ufd[2] = {
-        { .fd = fd,                           .events = POLLIN },
-        { .fd = vlc_object_waitpipe (p_this), .events = POLLIN },
-    };
+    struct pollfd ufd[2];
 
-    if (ufd[1].fd == -1)
-        return -1; /* vlc_object_waitpipe() sets errno */
+    ufd[0].fd = fd;
+    ufd[0].events = POLLIN;
+    ufd[1].fd = vlc_object_waitpipe (p_this);
+    ufd[1].events = POLLIN;
 
-    while (i_buflen > 0)
-    {
-        ufd[0].revents = ufd[1].revents = 0;
-
-        if (poll (ufd, sizeof (ufd) / sizeof (ufd[0]), -1) < 0)
-        {
-            if (errno != EINTR)
-                goto error;
-            continue;
-        }
-
-#ifndef POLLRDHUP /* This is nice but non-portable */
-# define POLLRDHUP 0
-#endif
-        if (i_total > 0)
-        {
-            /* Errors (-1) and EOF (0) will be returned on next call,
-             * otherwise we'd "hide" the error from the caller, which is a
-             * bad idea™. */
-            if (ufd[0].revents & (POLLERR|POLLNVAL|POLLRDHUP))
-                break;
-            if (ufd[1].revents)
-                break;
-        }
-        else
-        {
-            if (ufd[1].revents)
-            {
-                assert (p_this->b_die);
-                msg_Dbg (p_this, "socket %d polling interrupted", fd);
-#if defined(WIN32) || defined(UNDER_CE)
-                WSASetLastError (WSAEINTR);
-#else
-                errno = EINTR;
+    size_t i_total = 0;
+#if VLC_WINSTORE_APP
+    /* With winrtsock winsocks emulation library, the first call to read()
+     * before poll() starts an asynchronous transfer and returns 0.
+     * Always call poll() first.
+     *
+     * However if we have a virtual socket handler, try to read() first.
+     * See bug #8972 for details.
+     */
+    if (vs == NULL)
+        goto do_poll;
 #endif
-                goto silent;
-            }
-        }
-
-        assert (ufd[0].revents);
-
+    do
+    {
         ssize_t n;
         if (vs != NULL)
         {
@@ -315,69 +291,77 @@ net_Read (vlc_object_t *restrict p_this, int fd, const v_socket_t *vs,
         }
         else
         {
-#ifdef WIN32
+#ifdef _WIN32
             n = recv (fd, p_buf, i_buflen, 0);
 #else
             n = read (fd, p_buf, i_buflen);
 #endif
         }
 
-        if (n == -1)
+        if (n < 0)
         {
-#if defined(WIN32) || defined(UNDER_CE)
-            switch (WSAGetLastError ())
-            {
-                case WSAEWOULDBLOCK:
-                case WSAEINTR:
-                /* only happens with vs != NULL (TLS) - not really an error */
-                    continue;
-
-                case WSAEMSGSIZE:
-                /* For UDP only */
-                /* On Win32, recv() fails if the datagram doesn't fit inside
-                 * the passed buffer, even though the buffer will be filled
-                 * with the first part of the datagram. */
-                    msg_Err (p_this, "Receive error: "
-                                     "Increase the mtu size (--mtu option)");
-                    n = i_buflen;
-                    break;
-            }
-#else
-            switch (errno)
+            switch (net_errno)
             {
-                case EAGAIN: /* spurious wakeup or no TLS data */
+                case EAGAIN: /* no data */
 #if (EAGAIN != EWOULDBLOCK)
                 case EWOULDBLOCK:
 #endif
+                    break;
+#ifndef _WIN32
                 case EINTR:  /* asynchronous signal */
                     continue;
+#else
+                case WSAEMSGSIZE: /* datagram too big */
+                    n = i_buflen;
+                    break;
+#endif
+                default:
+                    goto error;
             }
+        }
+        else
+        if (n > 0)
+        {
+            i_total += n;
+            p_buf = (char *)p_buf + n;
+            i_buflen -= n;
+
+            if (!waitall || i_buflen == 0)
+                break;
+        }
+        else /* n == 0 */
+            break;/* end of stream or empty packet */
+
+        if (ufd[1].fd == -1)
+        {
+            errno = EINTR;
+            return -1;
+        }
+#if VLC_WINSTORE_APP
+do_poll:
 #endif
+        /* Wait for more data */
+        if (poll (ufd, sizeof (ufd) / sizeof (ufd[0]), -1) < 0)
+        {
+            if (errno == EINTR)
+                continue;
             goto error;
         }
 
-        if (n == 0)
-            /* For streams, this means end of file, and there will not be any
-             * further data ever on the stream. For datagram sockets, this
-             * means empty datagram, and there could be more data coming.
-             * However, it makes no sense to set <waitall> with datagrams in the
-             * first place.
-             */
-            break; // EOF
-
-        i_total += n;
-        p_buf = (char *)p_buf + n;
-        i_buflen -= n;
+        if (ufd[1].revents)
+        {
+            msg_Dbg (p_this, "socket %d polling interrupted", fd);
+            errno = EINTR;
+            return -1;
+        }
 
-        if (!waitall)
-            break;
+        assert (ufd[0].revents);
     }
+    while (i_buflen > 0);
 
     return i_total;
-
 error:
-    msg_Err (p_this, "Read error: %m");
-silent:
+    msg_Err (p_this, "read error: %s", vlc_strerror_c(errno));
     return -1;
 }
 
@@ -417,7 +401,7 @@ ssize_t net_Write( vlc_object_t *p_this, int fd, const v_socket_t *p_vs,
         {
             if (errno == EINTR)
                 continue;
-            msg_Err (p_this, "Polling error: %m");
+            msg_Err (p_this, "Polling error: %s", vlc_strerror_c(errno));
             return -1;
         }
 
@@ -434,7 +418,6 @@ ssize_t net_Write( vlc_object_t *p_this, int fd, const v_socket_t *p_vs,
         {
             if (ufd[1].revents)
             {
-                assert (p_this->b_die);
                 errno = EINTR;
                 goto error;
             }
@@ -443,7 +426,7 @@ ssize_t net_Write( vlc_object_t *p_this, int fd, const v_socket_t *p_vs,
         if (p_vs != NULL)
             val = p_vs->pf_send (p_vs->p_sys, p_data, i_data);
         else
-#ifdef WIN32
+#ifdef _WIN32
             val = send (fd, p_data, i_data, 0);
 #else
             val = write (fd, p_data, i_data);
@@ -453,7 +436,7 @@ ssize_t net_Write( vlc_object_t *p_this, int fd, const v_socket_t *p_vs,
         {
             if (errno == EINTR)
                 continue;
-            msg_Err (p_this, "Write error: %m");
+            msg_Err (p_this, "Write error: %s", vlc_strerror_c(errno));
             break;
         }
 
@@ -478,46 +461,47 @@ error:
  * This function is not thread-safe; the same file descriptor I/O cannot be
  * read by another thread at the same time (although it can be written to).
  *
+ * @note This only works with stream-oriented file descriptors, not with
+ * datagram or packet-oriented ones.
+ *
  * @return nul-terminated heap-allocated string, or NULL on I/O error.
  */
-char *net_Gets( vlc_object_t *p_this, int fd, const v_socket_t *p_vs )
+char *net_Gets(vlc_object_t *obj, int fd, const v_socket_t *vs)
 {
-    char *psz_line = NULL, *ptr = NULL;
-    size_t  i_line = 0, i_max = 0;
+    char *buf = NULL;
+    size_t bufsize = 0, buflen = 0;
 
-
-    for( ;; )
+    for (;;)
     {
-        if( i_line == i_max )
+        if (buflen == bufsize)
         {
-            i_max += 1024;
-            psz_line = xrealloc( psz_line, i_max );
-            ptr = psz_line + i_line;
-        }
+            if (unlikely(bufsize >= (1 << 16)))
+                goto error; /* put sane buffer size limit */
 
-        if( net_Read( p_this, fd, p_vs, ptr, 1, true ) != 1 )
-        {
-            if( i_line == 0 )
-            {
-                free( psz_line );
-                return NULL;
-            }
-            break;
+            char *newbuf = realloc(buf, bufsize + 1024);
+            if (unlikely(newbuf == NULL))
+                goto error;
+            buf = newbuf;
+            bufsize += 1024;
         }
 
-        if ( *ptr == '\n' )
+        ssize_t val = net_Read(obj, fd, vs, buf + buflen, 1, false);
+        if (val < 1)
+            goto error;
+
+        if (buf[buflen] == '\n')
             break;
 
-        i_line++;
-        ptr++;
+        buflen++;
     }
 
-    *ptr-- = '\0';
-
-    if( ( ptr >= psz_line ) && ( *ptr == '\r' ) )
-        *ptr = '\0';
-
-    return psz_line;
+    buf[buflen] = '\0';
+    if (buflen > 0 && buf[buflen - 1] == '\r')
+        buf[buflen - 1] = '\0';
+    return buf;
+error:
+    free(buf);
+    return NULL;
 }
 
 #undef net_Printf