/*****************************************************************************
* 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$
*
* 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_SYS_TIME_H
-# include <sys/time.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
extern int rootwrap_bind (int family, int socktype, int protocol,
const struct sockaddr *addr, size_t alen);
-int net_SetupSocket (int fd)
-{
-#if defined (WIN32) || defined (UNDER_CE)
- ioctlsocket (fd, FIONBIO, &(unsigned long){ 1 });
-#else
- fcntl (fd, F_SETFD, FD_CLOEXEC);
- fcntl (fd, F_SETFL, fcntl (fd, F_GETFL, 0) | O_NONBLOCK);
-#endif
-
- setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof (int));
- return 0;
-}
-
-
int net_Socket (vlc_object_t *p_this, int family, int socktype,
int protocol)
{
- int fd = socket (family, socktype, protocol);
+ int fd = vlc_socket (family, socktype, protocol, true);
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;
}
- net_SetupSocket (fd);
+ setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof (int));
#ifdef IPV6_V6ONLY
/*
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
#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,
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;
- int socktype = SOCK_DGRAM;
-
- switch( protocol )
- {
- case IPPROTO_TCP:
- socktype = SOCK_STREAM;
- break;
- case 33: /* DCCP */
-#ifdef __linux__
-# ifndef SOCK_DCCP
-# define SOCK_DCCP 6
-# endif
- socktype = SOCK_DCCP;
-#endif
- break;
- }
-
- memset (&hints, 0, sizeof( hints ));
- /* Since we use port numbers rather than service names, the socket type
- * does not really matter. */
- hints.ai_socktype = SOCK_DGRAM;
- 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,
- vlc_gai_strerror (i_val));
+ msg_Err (p_this, "Cannot resolve %s port %d : %s",
+ (psz_host != NULL) ? psz_host : "", i_port,
+ gai_strerror (i_val));
return NULL;
}
for (struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next)
{
- int fd = net_Socket (p_this, ptr->ai_family, socktype, protocol);
+ int fd = net_Socket (p_this, ptr->ai_family, ptr->ai_socktype,
+ 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
if (bind (fd, ptr->ai_addr, ptr->ai_addrlen))
{
net_Close (fd);
-#if !defined(WIN32) && !defined(UNDER_CE)
- fd = rootwrap_bind (ptr->ai_family, socktype,
- protocol ? protocol : ptr->ai_protocol,
+#if !defined(_WIN32)
+ fd = rootwrap_bind (ptr->ai_family, ptr->ai_socktype,
+ ptr->ai_protocol,
ptr->ai_addr, ptr->ai_addrlen);
if (fd != -1)
{
else
#endif
{
- msg_Err (p_this, "socket bind error (%m)");
+ msg_Err (p_this, "socket bind error: %s",
+ vlc_strerror_c(net_errno));
continue;
}
}
}
/* Listen */
- switch (socktype)
+ switch (ptr->ai_socktype)
{
case SOCK_STREAM:
case SOCK_RDM:
#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;
}
net_Close (fd);
}
- vlc_freeaddrinfo (res);
+ freeaddrinfo (res);
if (sockv != NULL)
sockv[sockc] = -1;
return sockv;
}
-
+#undef net_Read
/*****************************************************************************
- * __net_Read:
+ * net_Read:
*****************************************************************************
* Reads from a network socket. Cancellation point.
* If waitall is true, then we repeat until we have read the right amount of
* object has been signaled.
*****************************************************************************/
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)
+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)
{
}
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:
- /* 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;
}
-
-/* Write exact amount requested */
-ssize_t __net_Write( vlc_object_t *p_this, int fd, const v_socket_t *p_vs,
- const void *restrict p_data, size_t i_data )
+#undef net_Write
+/**
+ * Writes data to a file descriptor.
+ * This blocks until all data is written or an error occurs.
+ *
+ * This function is a cancellation point if p_vs is NULL.
+ * This function is not cancellation-safe if p_vs is not NULL.
+ *
+ * @return the total number of bytes written, or -1 if an error occurs
+ * before any data is written.
+ */
+ssize_t net_Write( vlc_object_t *p_this, int fd, const v_socket_t *p_vs,
+ const void *restrict p_data, size_t i_data )
{
size_t i_total = 0;
struct pollfd ufd[2] = {
{ .fd = vlc_object_waitpipe (p_this), .events = POLLIN },
};
- if (ufd[1].fd == -1)
+ if (unlikely(ufd[1].fd == -1))
+ {
+ vlc_testcancel ();
return -1;
+ }
while( i_data > 0 )
{
{
if (errno == EINTR)
continue;
- msg_Err (p_this, "Polling error: %m");
+ msg_Err (p_this, "Polling error: %s", vlc_strerror_c(errno));
return -1;
}
{
if (ufd[1].revents)
{
- assert (p_this->b_die);
errno = EINTR;
goto error;
}
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);
{
if (errno == EINTR)
continue;
- msg_Err (p_this, "Write error: %m");
+ msg_Err (p_this, "Write error: %s", vlc_strerror_c(errno));
break;
}
i_total += val;
}
+ if (unlikely(i_data == 0))
+ vlc_testcancel (); /* corner case */
+
if ((i_total > 0) || (i_data == 0))
return i_total;
return -1;
}
+#undef net_Gets
/**
* Reads a line from a file descriptor.
- * This function is not thread-safe; the same file descriptor cI/O annot be read
- * by another thread at the same time (although it can be written to).
+ * 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)
{
- char *psz_tmp;
- i_max += 1024;
- psz_tmp = realloc( psz_line, i_max );
- if( !psz_tmp )
- {
- free( psz_line );
- return NULL;
- }
- psz_line = psz_tmp;
- 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
ssize_t net_Printf( vlc_object_t *p_this, int fd, const v_socket_t *p_vs,
const char *psz_fmt, ... )
{
return i_ret;
}
-ssize_t __net_vaPrintf( vlc_object_t *p_this, int fd, const v_socket_t *p_vs,
- const char *psz_fmt, va_list args )
+#undef net_vaPrintf
+ssize_t net_vaPrintf( vlc_object_t *p_this, int fd, const v_socket_t *p_vs,
+ const char *psz_fmt, va_list args )
{
char *psz;
int i_ret;
int i_size = vasprintf( &psz, psz_fmt, args );
if( i_size == -1 )
return -1;
- i_ret = __net_Write( p_this, fd, p_vs, psz, i_size ) < i_size
+ i_ret = net_Write( p_this, fd, p_vs, psz, i_size ) < i_size
? -1 : i_size;
free( psz );