2004-01-05 15:10:58 +01:00
|
|
|
/*****************************************************************************
|
2005-12-05 10:36:39 +01:00
|
|
|
* io.c: network I/O functions
|
2004-01-05 15:10:58 +01:00
|
|
|
*****************************************************************************
|
2005-07-09 08:17:09 +02:00
|
|
|
* Copyright (C) 2004-2005 the VideoLAN team
|
2006-09-08 19:51:58 +02:00
|
|
|
* Copyright © 2005-2006 Rémi Denis-Courmont
|
2004-03-08 18:08:46 +01:00
|
|
|
* $Id$
|
2004-01-05 15:10:58 +01:00
|
|
|
*
|
|
|
|
* Authors: Laurent Aimar <fenrir@videolan.org>
|
2005-05-22 13:24:08 +02:00
|
|
|
* Rémi Denis-Courmont <rem # videolan.org>
|
2004-01-05 15:10:58 +01:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
* (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.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
2006-01-13 00:10:04 +01:00
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
2004-01-05 15:10:58 +01:00
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* Preamble
|
|
|
|
*****************************************************************************/
|
2006-09-12 20:49:41 +02:00
|
|
|
|
2004-01-05 15:10:58 +01:00
|
|
|
#include <vlc/vlc.h>
|
|
|
|
|
2006-09-12 20:49:41 +02:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
2006-11-05 17:03:04 +01:00
|
|
|
#include <limits.h>
|
2006-09-12 20:49:41 +02:00
|
|
|
|
2004-10-04 15:34:42 +02:00
|
|
|
#include <errno.h>
|
2006-09-08 19:51:58 +02:00
|
|
|
#include <assert.h>
|
2004-01-05 15:10:58 +01:00
|
|
|
|
|
|
|
#ifdef HAVE_FCNTL_H
|
|
|
|
# include <fcntl.h>
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_TIME_H
|
|
|
|
# include <sys/time.h>
|
|
|
|
#endif
|
2004-01-09 13:23:47 +01:00
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
|
# include <unistd.h>
|
|
|
|
#endif
|
2006-09-08 19:51:58 +02:00
|
|
|
#ifdef HAVE_POLL
|
2006-09-09 15:50:07 +02:00
|
|
|
# include <poll.h>
|
2006-09-08 19:51:58 +02:00
|
|
|
#endif
|
2004-01-09 13:23:47 +01:00
|
|
|
|
2004-01-05 15:10:58 +01:00
|
|
|
#include "network.h"
|
|
|
|
|
2004-11-15 18:07:06 +01:00
|
|
|
#ifndef INADDR_ANY
|
|
|
|
# define INADDR_ANY 0x00000000
|
|
|
|
#endif
|
|
|
|
#ifndef INADDR_NONE
|
|
|
|
# define INADDR_NONE 0xFFFFFFFF
|
|
|
|
#endif
|
|
|
|
|
2005-07-04 18:42:22 +02:00
|
|
|
#if defined(WIN32) || defined(UNDER_CE)
|
2006-10-19 17:05:35 +02:00
|
|
|
# undef EAFNOSUPPORT
|
|
|
|
# define EAFNOSUPPORT WSAEAFNOSUPPORT
|
2005-07-04 18:42:22 +02:00
|
|
|
#endif
|
|
|
|
|
2006-11-05 16:50:31 +01:00
|
|
|
extern int rootwrap_bind (int family, int socktype, int protocol,
|
|
|
|
const struct sockaddr *addr, size_t alen);
|
|
|
|
|
2006-10-19 17:05:35 +02:00
|
|
|
int net_Socket (vlc_object_t *p_this, int family, int socktype,
|
|
|
|
int protocol)
|
|
|
|
{
|
|
|
|
int fd = socket (family, socktype, protocol);
|
|
|
|
if (fd == -1)
|
2005-07-04 18:42:22 +02:00
|
|
|
{
|
2006-10-19 17:05:35 +02:00
|
|
|
if (net_errno != EAFNOSUPPORT)
|
|
|
|
msg_Err (p_this, "cannot create socket: %s",
|
|
|
|
net_strerror (net_errno));
|
|
|
|
return -1;
|
2005-07-04 18:42:22 +02:00
|
|
|
}
|
2006-10-19 17:05:35 +02:00
|
|
|
|
|
|
|
#if defined (WIN32) || defined (UNDER_CE)
|
|
|
|
ioctlsocket (fd, FIONBIO, &(unsigned long){ 1 });
|
2005-07-04 18:42:22 +02:00
|
|
|
#else
|
2006-10-19 17:05:35 +02:00
|
|
|
fcntl (fd, F_SETFD, FD_CLOEXEC);
|
|
|
|
fcntl (fd, F_SETFL, fcntl (fd, F_GETFL, 0) | O_NONBLOCK);
|
2005-07-04 18:42:22 +02:00
|
|
|
#endif
|
|
|
|
|
2006-10-19 17:05:35 +02:00
|
|
|
setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof (int));
|
2005-07-04 18:42:22 +02:00
|
|
|
|
|
|
|
#ifdef IPV6_V6ONLY
|
|
|
|
/*
|
|
|
|
* Accepts only IPv6 connections on IPv6 sockets
|
|
|
|
* (and open an IPv4 socket later as well if needed).
|
|
|
|
* Only Linux and FreeBSD can map IPv4 connections on IPv6 sockets,
|
|
|
|
* so this allows for more uniform handling across platforms. Besides,
|
|
|
|
* it makes sure that IPv4 addresses will be printed as w.x.y.z rather
|
|
|
|
* than ::ffff:w.x.y.z
|
|
|
|
*/
|
2006-10-19 17:05:35 +02:00
|
|
|
if (family == AF_INET6)
|
|
|
|
setsockopt (fd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){ 1 }, sizeof (int));
|
2005-07-04 18:42:22 +02:00
|
|
|
#endif
|
|
|
|
|
2006-10-19 17:05:35 +02:00
|
|
|
#if defined (WIN32) || defined (UNDER_CE)
|
2005-08-21 13:27:15 +02:00
|
|
|
# ifndef IPV6_PROTECTION_LEVEL
|
|
|
|
# define IPV6_PROTECTION_LEVEL 23
|
2006-10-19 17:05:35 +02:00
|
|
|
# define PROTECTION_LEVEL_UNRESTRICTED 30
|
2005-08-21 13:27:15 +02:00
|
|
|
# endif
|
2006-10-19 17:05:35 +02:00
|
|
|
if (family == AF_INET6)
|
|
|
|
setsockopt (fd, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL,
|
|
|
|
&(int){ PROTECTION_LEVEL_UNRESTRICTED }, sizeof (int));
|
2005-07-04 18:42:22 +02:00
|
|
|
#endif
|
2006-10-19 17:05:35 +02:00
|
|
|
|
2005-07-04 18:42:22 +02:00
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2004-01-21 11:22:31 +01:00
|
|
|
|
2006-11-06 18:29:07 +01:00
|
|
|
static int *net_Listen (vlc_object_t *p_this, const char *psz_host,
|
|
|
|
int i_port, int family, int socktype, int protocol)
|
2006-11-05 16:50:31 +01:00
|
|
|
{
|
|
|
|
struct addrinfo hints, *res;
|
|
|
|
|
|
|
|
memset (&hints, 0, sizeof( hints ));
|
|
|
|
hints.ai_family = family;
|
|
|
|
hints.ai_socktype = socktype;
|
|
|
|
hints.ai_protocol = protocol;
|
|
|
|
hints.ai_flags = AI_PASSIVE;
|
|
|
|
|
|
|
|
msg_Dbg (p_this, "net: listening to %s port %d", psz_host, i_port);
|
|
|
|
|
|
|
|
int i_val = vlc_getaddrinfo (p_this, 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));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int *sockv = NULL;
|
|
|
|
unsigned sockc = 0;
|
|
|
|
|
|
|
|
for (struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next)
|
|
|
|
{
|
|
|
|
int fd = net_Socket (p_this, ptr->ai_family, ptr->ai_socktype,
|
|
|
|
ptr->ai_protocol);
|
|
|
|
if (fd == -1)
|
|
|
|
{
|
|
|
|
msg_Dbg (p_this, "socket error: %s", net_strerror (net_errno));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Bind the socket */
|
2006-11-05 21:05:48 +01:00
|
|
|
#if 0//defined (WIN32) || defined (UNDER_CE)
|
|
|
|
/*
|
|
|
|
* Under Win32 and for multicasting, we bind to INADDR_ANY.
|
|
|
|
* This is of course a severe bug, since the socket would logically
|
|
|
|
* receive unicast traffic, and multicast traffic of groups subscribed
|
|
|
|
* to via other sockets.
|
|
|
|
*/
|
|
|
|
if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen)
|
|
|
|
&& (sizeof (struct sockaddr_storage) >= ptr->ai_addrlen))
|
|
|
|
{
|
|
|
|
struct sockaddr_storage dumb =
|
|
|
|
{
|
|
|
|
.ss_family = ptr->ai_addr->sa_family
|
|
|
|
};
|
|
|
|
|
|
|
|
bind (fd, (struct sockaddr *)&dumb, ptr->ai_addrlen);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
2006-11-05 16:50:31 +01:00
|
|
|
if (bind (fd, ptr->ai_addr, ptr->ai_addrlen))
|
|
|
|
{
|
|
|
|
int saved_errno = net_errno;
|
|
|
|
|
|
|
|
net_Close (fd);
|
|
|
|
#if !defined(WIN32) && !defined(UNDER_CE)
|
|
|
|
fd = rootwrap_bind (ptr->ai_family, ptr->ai_socktype,
|
|
|
|
ptr->ai_protocol, ptr->ai_addr,
|
|
|
|
ptr->ai_addrlen);
|
|
|
|
if (fd != -1)
|
|
|
|
{
|
|
|
|
msg_Dbg (p_this, "got socket %d from rootwrap", fd);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
msg_Err (p_this, "socket bind error (%s)",
|
|
|
|
net_strerror( saved_errno ) );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-11-05 20:20:52 +01:00
|
|
|
if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen))
|
|
|
|
{
|
|
|
|
if (net_Subscribe (p_this, fd, ptr->ai_addr, ptr->ai_addrlen))
|
|
|
|
{
|
|
|
|
net_Close (fd);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-11-05 16:50:31 +01:00
|
|
|
/* Listen */
|
|
|
|
switch (ptr->ai_socktype)
|
|
|
|
{
|
|
|
|
case SOCK_STREAM:
|
|
|
|
case SOCK_RDM:
|
|
|
|
case SOCK_SEQPACKET:
|
|
|
|
if (listen (fd, INT_MAX))
|
|
|
|
{
|
|
|
|
msg_Err (p_this, "socket listen error (%s)",
|
|
|
|
net_strerror (net_errno));
|
|
|
|
net_Close (fd);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int *nsockv = (int *)realloc (sockv, (sockc + 2) * sizeof (int));
|
|
|
|
if (nsockv != NULL)
|
|
|
|
{
|
|
|
|
nsockv[sockc++] = fd;
|
|
|
|
sockv = nsockv;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
net_Close (fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
vlc_freeaddrinfo (res);
|
|
|
|
|
|
|
|
if (sockv != NULL)
|
|
|
|
sockv[sockc] = -1;
|
|
|
|
|
|
|
|
return sockv;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-11-06 18:29:07 +01:00
|
|
|
int net_ListenSingle (vlc_object_t *obj, const char *host, int port,
|
|
|
|
int family, int socktype, int protocol)
|
|
|
|
{
|
|
|
|
int *fdv = net_Listen (obj, host, port, family, socktype, protocol);
|
|
|
|
if (fdv == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
for (unsigned i = 1; fdv[i] != -1; i++)
|
|
|
|
{
|
|
|
|
msg_Warn (obj, "A socket has been dropped!");
|
|
|
|
net_Close (fdv[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
int fd = fdv[0];
|
|
|
|
assert (fd != -1);
|
|
|
|
|
|
|
|
free (fdv);
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-01-05 15:10:58 +01:00
|
|
|
/*****************************************************************************
|
|
|
|
* __net_Close:
|
|
|
|
*****************************************************************************
|
|
|
|
* Close a network handle
|
|
|
|
*****************************************************************************/
|
2006-10-19 17:05:35 +02:00
|
|
|
void net_Close (int fd)
|
2004-01-05 15:10:58 +01:00
|
|
|
{
|
|
|
|
#ifdef UNDER_CE
|
2006-10-19 17:05:35 +02:00
|
|
|
CloseHandle ((HANDLE)fd);
|
|
|
|
#elif defined (WIN32)
|
|
|
|
closesocket (fd);
|
2004-01-05 15:10:58 +01:00
|
|
|
#else
|
2006-10-19 17:05:35 +02:00
|
|
|
(void)close (fd);
|
2004-01-05 15:10:58 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2006-09-08 19:51:58 +02:00
|
|
|
|
2006-10-19 17:27:58 +02:00
|
|
|
static ssize_t
|
2006-09-08 19:51:58 +02:00
|
|
|
net_ReadInner( vlc_object_t *restrict p_this, unsigned fdc, const int *fdv,
|
2006-09-08 20:14:25 +02:00
|
|
|
const v_socket_t *const *restrict vsv,
|
2006-09-12 14:17:41 +02:00
|
|
|
uint8_t *restrict p_buf, size_t i_buflen,
|
2006-09-08 19:51:58 +02:00
|
|
|
int wait_ms, vlc_bool_t waitall )
|
|
|
|
{
|
2006-09-12 14:17:41 +02:00
|
|
|
size_t i_total = 0;
|
2006-09-08 19:51:58 +02:00
|
|
|
|
2006-10-19 17:27:58 +02:00
|
|
|
while (i_buflen > 0)
|
2006-09-08 19:51:58 +02:00
|
|
|
{
|
2006-10-19 17:27:58 +02:00
|
|
|
unsigned i;
|
|
|
|
ssize_t n;
|
2006-09-12 14:17:41 +02:00
|
|
|
#ifdef HAVE_POLL
|
|
|
|
struct pollfd ufd[fdc];
|
|
|
|
#else
|
|
|
|
int maxfd = -1;
|
|
|
|
fd_set set;
|
|
|
|
#endif
|
|
|
|
|
2006-10-19 17:27:58 +02:00
|
|
|
int delay_ms = 500;
|
|
|
|
if ((wait_ms != -1) && (wait_ms < 500))
|
2006-09-08 19:56:58 +02:00
|
|
|
delay_ms = wait_ms;
|
2006-09-08 19:51:58 +02:00
|
|
|
|
2006-10-19 17:27:58 +02:00
|
|
|
if (p_this->b_die)
|
2006-10-19 16:55:57 +02:00
|
|
|
{
|
|
|
|
errno = EINTR;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2006-09-08 19:51:58 +02:00
|
|
|
#ifdef HAVE_POLL
|
2006-10-19 17:27:58 +02:00
|
|
|
memset (ufd, 0, sizeof (ufd));
|
2006-09-08 19:51:58 +02:00
|
|
|
|
2006-09-12 14:17:41 +02:00
|
|
|
for( i = 0; i < fdc; i++ )
|
2006-09-08 19:51:58 +02:00
|
|
|
{
|
|
|
|
ufd[i].fd = fdv[i];
|
|
|
|
ufd[i].events = POLLIN;
|
|
|
|
}
|
|
|
|
|
2006-10-19 16:55:57 +02:00
|
|
|
n = poll( ufd, fdc, delay_ms );
|
2006-09-08 19:51:58 +02:00
|
|
|
#else
|
|
|
|
FD_ZERO (&set);
|
|
|
|
|
2006-09-12 14:17:41 +02:00
|
|
|
for( i = 0; i < fdc; i++ )
|
2006-09-08 19:51:58 +02:00
|
|
|
{
|
|
|
|
#if !defined(WIN32) && !defined(UNDER_CE)
|
2006-09-12 14:17:41 +02:00
|
|
|
if( fdv[i] >= FD_SETSIZE )
|
2006-09-08 19:51:58 +02:00
|
|
|
{
|
|
|
|
/* We don't want to overflow select() fd_set */
|
|
|
|
msg_Err( p_this, "select set overflow" );
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#endif
|
2006-09-12 14:17:41 +02:00
|
|
|
FD_SET( fdv[i], &set );
|
|
|
|
if( fdv[i] > maxfd )
|
2006-09-08 19:51:58 +02:00
|
|
|
maxfd = fdv[i];
|
|
|
|
}
|
|
|
|
|
2006-09-12 14:17:41 +02:00
|
|
|
n = select( maxfd + 1, &set, NULL, NULL,
|
|
|
|
(wait_ms == -1) ? NULL
|
|
|
|
: &(struct timeval){ 0, delay_ms * 1000 } );
|
2006-10-19 17:27:58 +02:00
|
|
|
#endif
|
2006-09-12 14:17:41 +02:00
|
|
|
if( n == -1 )
|
2006-09-08 19:51:58 +02:00
|
|
|
goto error;
|
|
|
|
|
2006-10-19 17:27:58 +02:00
|
|
|
assert ((unsigned)n <= fdc);
|
2006-09-08 19:51:58 +02:00
|
|
|
|
2006-10-19 17:27:58 +02:00
|
|
|
if (n == 0) // timeout
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (i = 0;; i++)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_POLL
|
|
|
|
if ((i_total > 0) && (ufd[i].revents & POLLERR))
|
|
|
|
return i_total; // error will be dequeued on next run
|
|
|
|
|
|
|
|
if ((ufd[i].revents & POLLIN) == 0)
|
|
|
|
#else
|
|
|
|
if (!FD_ISSET (fdv[i], &set))
|
|
|
|
continue;
|
|
|
|
#endif
|
|
|
|
fdc = 1;
|
|
|
|
fdv += i;
|
|
|
|
vsv += i;
|
|
|
|
break;
|
|
|
|
}
|
2006-09-08 19:51:58 +02:00
|
|
|
|
2006-09-12 14:17:41 +02:00
|
|
|
if( (*vsv) != NULL )
|
|
|
|
{
|
|
|
|
n = (*vsv)->pf_recv( (*vsv)->p_sys, p_buf, i_buflen );
|
|
|
|
}
|
2006-09-08 19:51:58 +02:00
|
|
|
else
|
2006-09-12 14:17:41 +02:00
|
|
|
{
|
2006-09-09 12:50:29 +02:00
|
|
|
#if defined(WIN32) || defined(UNDER_CE)
|
2006-09-12 14:17:41 +02:00
|
|
|
n = recv( *fdv, p_buf, i_buflen, 0 );
|
2006-09-09 12:50:29 +02:00
|
|
|
#else
|
2006-09-12 14:17:41 +02:00
|
|
|
n = read( *fdv, p_buf, i_buflen );
|
2006-09-09 12:50:29 +02:00
|
|
|
#endif
|
2006-09-12 14:17:41 +02:00
|
|
|
}
|
2006-09-08 19:51:58 +02:00
|
|
|
|
2006-09-12 14:17:41 +02:00
|
|
|
if( n == -1 )
|
2006-09-08 19:51:58 +02:00
|
|
|
{
|
|
|
|
#if defined(WIN32) || defined(UNDER_CE)
|
2006-09-12 14:17:41 +02:00
|
|
|
switch( WSAGetLastError() )
|
2006-09-08 19:51:58 +02:00
|
|
|
{
|
|
|
|
case WSAEWOULDBLOCK:
|
|
|
|
/* only happens with vs != NULL (SSL) - 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. */
|
2006-09-09 15:50:07 +02:00
|
|
|
msg_Err( p_this, "Receive error: "
|
2006-09-08 19:51:58 +02:00
|
|
|
"Increase the mtu size (--mtu option)" );
|
2006-09-12 14:17:41 +02:00
|
|
|
i_total += i_buflen;
|
|
|
|
return i_total;
|
2006-09-08 19:51:58 +02:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
if( errno == EAGAIN ) /* spurious wake-up (sucks if fdc > 1) */
|
|
|
|
continue;
|
|
|
|
#endif
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2006-10-19 17:07:43 +02:00
|
|
|
if (n == 0) // EOF
|
|
|
|
return i_total;
|
|
|
|
|
2006-09-12 14:17:41 +02:00
|
|
|
i_total += n;
|
|
|
|
p_buf += n;
|
|
|
|
i_buflen -= n;
|
2006-09-08 19:56:58 +02:00
|
|
|
|
2006-10-19 17:27:58 +02:00
|
|
|
if (!waitall)
|
|
|
|
return i_total;
|
|
|
|
|
|
|
|
if (wait_ms != -1)
|
2006-09-12 14:17:41 +02:00
|
|
|
{
|
2006-09-08 19:56:58 +02:00
|
|
|
wait_ms -= delay_ms;
|
2006-10-19 17:27:58 +02:00
|
|
|
if (wait_ms == 0)
|
|
|
|
return i_total; // time's up!
|
2006-09-12 14:17:41 +02:00
|
|
|
}
|
2006-09-08 19:51:58 +02:00
|
|
|
}
|
2006-10-25 12:17:11 +02:00
|
|
|
return i_total;
|
2006-09-08 19:51:58 +02:00
|
|
|
|
|
|
|
error:
|
2006-10-26 16:58:56 +02:00
|
|
|
msg_Err( p_this, "Read error: %s", net_strerror (net_errno) );
|
2006-10-19 17:30:43 +02:00
|
|
|
return i_total ? (ssize_t)i_total : -1;
|
2006-09-08 19:51:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-01-05 15:10:58 +01:00
|
|
|
/*****************************************************************************
|
|
|
|
* __net_Read:
|
|
|
|
*****************************************************************************
|
|
|
|
* Read from a network socket
|
2005-07-15 17:24:39 +02:00
|
|
|
* If b_retry is true, then we repeat until we have read the right amount of
|
2004-01-05 15:10:58 +01:00
|
|
|
* data
|
|
|
|
*****************************************************************************/
|
2006-09-08 19:51:58 +02:00
|
|
|
int __net_Read( vlc_object_t *restrict p_this, int fd,
|
2006-09-08 20:14:25 +02:00
|
|
|
const v_socket_t *restrict p_vs,
|
2006-09-08 19:51:58 +02:00
|
|
|
uint8_t *restrict p_data, int i_data, vlc_bool_t b_retry )
|
2004-01-05 15:10:58 +01:00
|
|
|
{
|
2006-09-12 14:17:41 +02:00
|
|
|
return net_ReadInner( p_this, 1, &(int){ fd },
|
2006-09-08 20:14:25 +02:00
|
|
|
&(const v_socket_t *){ p_vs },
|
2006-09-12 14:17:41 +02:00
|
|
|
p_data, i_data, -1, b_retry );
|
2004-01-05 15:10:58 +01:00
|
|
|
}
|
|
|
|
|
2006-09-08 20:07:18 +02:00
|
|
|
|
* ALL: Better announce system
- The SAP handler now runs in a separate thread.
- RTP sessions can be announced with sap (sdp=sap://,name=...)
TODO: Make this more configurable
- Better SDP generation (the timestamp problem is not resolved)
About this, there is a problem : as, for a RTP session, the URI
is the complete SDP, if the session is recreated, as the URI has
changed, a new item is added to the playlist
- Experimental flow control algorithm :
It does not follow the "Recommended" implementation, as it needs
to count the sessions (to achieve this, we should make this work
together with the SAP listener)
It is disabled by default (use --sap-flow-control to enable).
When it is disabled, sap announcement interval is set by --sap-interval
* src/misc/net.c : created net_ReadNonBlock
* sap.c : Fixed memory problem
2004-04-18 20:21:09 +02:00
|
|
|
/*****************************************************************************
|
|
|
|
* __net_ReadNonBlock:
|
|
|
|
*****************************************************************************
|
|
|
|
* Read from a network socket, non blocking mode (with timeout)
|
|
|
|
*****************************************************************************/
|
2006-09-08 20:07:18 +02:00
|
|
|
int __net_ReadNonBlock( vlc_object_t *restrict p_this, int fd,
|
2006-09-08 20:14:25 +02:00
|
|
|
const v_socket_t *restrict p_vs,
|
2006-09-08 20:07:18 +02:00
|
|
|
uint8_t *restrict p_data, int i_data, mtime_t i_wait)
|
* ALL: Better announce system
- The SAP handler now runs in a separate thread.
- RTP sessions can be announced with sap (sdp=sap://,name=...)
TODO: Make this more configurable
- Better SDP generation (the timestamp problem is not resolved)
About this, there is a problem : as, for a RTP session, the URI
is the complete SDP, if the session is recreated, as the URI has
changed, a new item is added to the playlist
- Experimental flow control algorithm :
It does not follow the "Recommended" implementation, as it needs
to count the sessions (to achieve this, we should make this work
together with the SAP listener)
It is disabled by default (use --sap-flow-control to enable).
When it is disabled, sap announcement interval is set by --sap-interval
* src/misc/net.c : created net_ReadNonBlock
* sap.c : Fixed memory problem
2004-04-18 20:21:09 +02:00
|
|
|
{
|
2006-09-08 20:14:25 +02:00
|
|
|
return net_ReadInner (p_this, 1, &(int){ fd },
|
|
|
|
&(const v_socket_t *){ p_vs },
|
2006-09-08 20:07:18 +02:00
|
|
|
p_data, i_data, i_wait / 1000, VLC_FALSE);
|
* ALL: Better announce system
- The SAP handler now runs in a separate thread.
- RTP sessions can be announced with sap (sdp=sap://,name=...)
TODO: Make this more configurable
- Better SDP generation (the timestamp problem is not resolved)
About this, there is a problem : as, for a RTP session, the URI
is the complete SDP, if the session is recreated, as the URI has
changed, a new item is added to the playlist
- Experimental flow control algorithm :
It does not follow the "Recommended" implementation, as it needs
to count the sessions (to achieve this, we should make this work
together with the SAP listener)
It is disabled by default (use --sap-flow-control to enable).
When it is disabled, sap announcement interval is set by --sap-interval
* src/misc/net.c : created net_ReadNonBlock
* sap.c : Fixed memory problem
2004-04-18 20:21:09 +02:00
|
|
|
}
|
|
|
|
|
2006-09-08 20:07:18 +02:00
|
|
|
|
2004-11-06 12:13:23 +01:00
|
|
|
/*****************************************************************************
|
|
|
|
* __net_Select:
|
|
|
|
*****************************************************************************
|
2004-11-06 12:14:00 +01:00
|
|
|
* Read from several sockets (with timeout). Takes data from the first socket
|
|
|
|
* that has some.
|
2004-11-06 12:13:23 +01:00
|
|
|
*****************************************************************************/
|
2006-09-08 20:14:25 +02:00
|
|
|
int __net_Select( vlc_object_t *restrict p_this, const int *restrict pi_fd,
|
|
|
|
const v_socket_t *const *restrict pp_vs,
|
2006-09-08 20:07:18 +02:00
|
|
|
int i_fd, uint8_t *restrict p_data, int i_data,
|
|
|
|
mtime_t i_wait )
|
2004-11-06 12:13:23 +01:00
|
|
|
{
|
2006-09-12 14:17:41 +02:00
|
|
|
if( pp_vs == NULL )
|
2004-11-06 12:13:23 +01:00
|
|
|
{
|
2006-09-08 20:07:18 +02:00
|
|
|
const v_socket_t *vsv[i_fd];
|
2006-09-12 14:17:41 +02:00
|
|
|
memset( vsv, 0, sizeof (vsv) );
|
2004-11-06 12:13:23 +01:00
|
|
|
|
2006-09-12 14:17:41 +02:00
|
|
|
return net_ReadInner( p_this, i_fd, pi_fd, vsv, p_data, i_data,
|
|
|
|
i_wait / 1000, VLC_FALSE );
|
2004-11-06 12:13:23 +01:00
|
|
|
}
|
|
|
|
|
2006-09-12 14:17:41 +02:00
|
|
|
return net_ReadInner( p_this, i_fd, pi_fd, pp_vs, p_data, i_data,
|
|
|
|
i_wait / 1000, VLC_FALSE );
|
2004-11-06 12:13:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-01-05 15:10:58 +01:00
|
|
|
/* Write exact amount requested */
|
2006-09-08 20:14:25 +02:00
|
|
|
int __net_Write( vlc_object_t *p_this, int fd, const v_socket_t *p_vs,
|
2005-08-18 17:24:38 +02:00
|
|
|
const uint8_t *p_data, int i_data )
|
2004-01-05 15:10:58 +01:00
|
|
|
{
|
2006-09-12 14:19:33 +02:00
|
|
|
size_t i_total = 0;
|
2004-01-05 15:10:58 +01:00
|
|
|
|
2006-09-12 14:17:41 +02:00
|
|
|
while( i_data > 0 )
|
2004-01-05 15:10:58 +01:00
|
|
|
{
|
2006-09-12 14:17:41 +02:00
|
|
|
if( p_this->b_die )
|
2006-09-12 14:19:33 +02:00
|
|
|
return i_total;
|
2004-01-05 15:10:58 +01:00
|
|
|
|
2006-09-09 15:50:07 +02:00
|
|
|
#ifdef HAVE_POLL
|
|
|
|
struct pollfd ufd[1];
|
|
|
|
memset (ufd, 0, sizeof (ufd));
|
|
|
|
ufd[0].fd = fd;
|
|
|
|
ufd[0].events = POLLOUT;
|
2004-03-08 18:08:46 +01:00
|
|
|
|
2006-09-09 15:50:07 +02:00
|
|
|
int val = poll (ufd, 1, 500);
|
2006-09-12 14:19:33 +02:00
|
|
|
if ((val > 0) && (ufd[0].revents & POLLERR) && (i_total > 0))
|
|
|
|
return i_total; // error will be dequeued separately on next call
|
2006-09-09 15:50:07 +02:00
|
|
|
#else
|
|
|
|
fd_set set;
|
|
|
|
FD_ZERO (&set);
|
2004-01-05 15:10:58 +01:00
|
|
|
|
2006-09-09 15:50:07 +02:00
|
|
|
#if !defined(WIN32) && !defined(UNDER_CE)
|
|
|
|
if (fd >= FD_SETSIZE)
|
2004-01-05 15:10:58 +01:00
|
|
|
{
|
2006-09-09 15:50:07 +02:00
|
|
|
/* We don't want to overflow select() fd_set */
|
|
|
|
msg_Err (p_this, "select set overflow");
|
|
|
|
return -1;
|
2004-01-05 15:10:58 +01:00
|
|
|
}
|
2006-09-09 15:50:07 +02:00
|
|
|
#endif
|
|
|
|
FD_SET (fd, &set);
|
2004-01-05 15:10:58 +01:00
|
|
|
|
2006-09-09 15:50:07 +02:00
|
|
|
int val = select (fd + 1, NULL, &set, NULL,
|
|
|
|
&(struct timeval){ 0, 500000 });
|
|
|
|
#endif
|
|
|
|
switch (val)
|
2004-01-05 15:10:58 +01:00
|
|
|
{
|
2006-09-09 15:50:07 +02:00
|
|
|
case -1:
|
|
|
|
if (errno != EINTR)
|
|
|
|
{
|
|
|
|
msg_Err (p_this, "Write error: %s",
|
|
|
|
net_strerror (net_errno));
|
2006-09-12 14:19:33 +02:00
|
|
|
return i_total ? (int)i_total : -1;
|
2006-09-09 15:50:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
case 0:
|
|
|
|
continue;
|
2004-01-05 15:10:58 +01:00
|
|
|
}
|
|
|
|
|
2006-09-09 15:50:07 +02:00
|
|
|
if (p_vs != NULL)
|
|
|
|
val = p_vs->pf_send (p_vs->p_sys, p_data, i_data);
|
|
|
|
else
|
|
|
|
#if defined(WIN32) || defined(UNDER_CE)
|
2006-09-16 14:45:08 +02:00
|
|
|
val = send (fd, p_data, i_data, 0);
|
2006-09-09 15:50:07 +02:00
|
|
|
#else
|
|
|
|
val = write (fd, p_data, i_data);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (val == -1)
|
2006-09-12 14:19:33 +02:00
|
|
|
return i_total ? (int)i_total : -1;
|
2006-09-09 15:50:07 +02:00
|
|
|
if (val == 0)
|
2006-09-12 14:19:33 +02:00
|
|
|
return i_total;
|
2006-09-09 15:50:07 +02:00
|
|
|
|
|
|
|
p_data += val;
|
|
|
|
i_data -= val;
|
2006-09-12 14:19:33 +02:00
|
|
|
i_total += val;
|
2004-01-05 15:10:58 +01:00
|
|
|
}
|
2006-09-09 15:50:07 +02:00
|
|
|
|
2006-09-12 14:19:33 +02:00
|
|
|
return i_total;
|
2004-01-05 15:10:58 +01:00
|
|
|
}
|
|
|
|
|
2006-09-08 20:14:25 +02:00
|
|
|
char *__net_Gets( vlc_object_t *p_this, int fd, const v_socket_t *p_vs )
|
2004-01-05 15:10:58 +01:00
|
|
|
{
|
2005-07-05 18:44:56 +02:00
|
|
|
char *psz_line = NULL, *ptr = NULL;
|
|
|
|
size_t i_line = 0, i_max = 0;
|
2004-01-05 15:10:58 +01:00
|
|
|
|
|
|
|
|
|
|
|
for( ;; )
|
|
|
|
{
|
2005-07-05 18:44:56 +02:00
|
|
|
if( i_line == i_max )
|
2004-01-05 15:10:58 +01:00
|
|
|
{
|
2005-07-05 18:44:56 +02:00
|
|
|
i_max += 1024;
|
|
|
|
psz_line = realloc( psz_line, i_max );
|
|
|
|
ptr = psz_line + i_line;
|
2004-01-05 15:10:58 +01:00
|
|
|
}
|
2004-01-07 00:03:17 +01:00
|
|
|
|
2005-07-10 13:49:42 +02:00
|
|
|
if( net_Read( p_this, fd, p_vs, (uint8_t *)ptr, 1, VLC_TRUE ) != 1 )
|
2004-01-05 15:10:58 +01:00
|
|
|
{
|
2005-07-05 18:44:56 +02:00
|
|
|
if( i_line == 0 )
|
|
|
|
{
|
|
|
|
free( psz_line );
|
|
|
|
return NULL;
|
|
|
|
}
|
2004-01-05 15:10:58 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-07-05 18:44:56 +02:00
|
|
|
if ( *ptr == '\n' )
|
|
|
|
break;
|
2004-01-05 15:10:58 +01:00
|
|
|
|
2005-07-05 18:44:56 +02:00
|
|
|
i_line++;
|
|
|
|
ptr++;
|
2004-01-05 15:10:58 +01:00
|
|
|
}
|
|
|
|
|
2005-07-05 18:44:56 +02:00
|
|
|
*ptr-- = '\0';
|
|
|
|
|
2005-07-05 20:28:26 +02:00
|
|
|
if( ( ptr >= psz_line ) && ( *ptr == '\r' ) )
|
2005-07-05 18:44:56 +02:00
|
|
|
*ptr = '\0';
|
|
|
|
|
2004-01-05 15:10:58 +01:00
|
|
|
return psz_line;
|
|
|
|
}
|
|
|
|
|
2006-09-08 20:14:25 +02:00
|
|
|
int net_Printf( vlc_object_t *p_this, int fd, const v_socket_t *p_vs,
|
2004-11-07 12:02:59 +01:00
|
|
|
const char *psz_fmt, ... )
|
2004-01-05 15:10:58 +01:00
|
|
|
{
|
2004-07-25 00:49:28 +02:00
|
|
|
int i_ret;
|
2004-01-05 15:10:58 +01:00
|
|
|
va_list args;
|
2004-07-25 00:49:28 +02:00
|
|
|
va_start( args, psz_fmt );
|
2004-11-07 12:02:59 +01:00
|
|
|
i_ret = net_vaPrintf( p_this, fd, p_vs, psz_fmt, args );
|
2004-07-25 00:49:28 +02:00
|
|
|
va_end( args );
|
|
|
|
|
|
|
|
return i_ret;
|
|
|
|
}
|
|
|
|
|
2006-09-08 20:14:25 +02:00
|
|
|
int __net_vaPrintf( vlc_object_t *p_this, int fd, const v_socket_t *p_vs,
|
2004-11-07 12:02:59 +01:00
|
|
|
const char *psz_fmt, va_list args )
|
2004-07-25 00:49:28 +02:00
|
|
|
{
|
2004-01-05 15:10:58 +01:00
|
|
|
char *psz;
|
2004-01-07 15:59:03 +01:00
|
|
|
int i_size, i_ret;
|
2004-01-05 15:10:58 +01:00
|
|
|
|
2005-07-15 17:24:39 +02:00
|
|
|
i_size = vasprintf( &psz, psz_fmt, args );
|
2005-07-10 13:49:42 +02:00
|
|
|
i_ret = __net_Write( p_this, fd, p_vs, (uint8_t *)psz, i_size ) < i_size
|
|
|
|
? -1 : i_size;
|
2004-01-07 15:59:03 +01:00
|
|
|
free( psz );
|
|
|
|
|
|
|
|
return i_ret;
|
2004-01-05 15:10:58 +01:00
|
|
|
}
|
2004-11-15 18:07:06 +01:00
|
|
|
|
|
|
|
|
2005-07-28 17:31:10 +02:00
|
|
|
/*****************************************************************************
|
|
|
|
* inet_pton replacement for obsolete and/or crap operating systems
|
|
|
|
*****************************************************************************/
|
|
|
|
#ifndef HAVE_INET_PTON
|
|
|
|
int inet_pton(int af, const char *src, void *dst)
|
|
|
|
{
|
|
|
|
# ifdef WIN32
|
|
|
|
/* As we already know, Microsoft always go its own way, so even if they do
|
|
|
|
* provide IPv6, they don't provide the API. */
|
|
|
|
struct sockaddr_storage addr;
|
|
|
|
int len = sizeof( addr );
|
|
|
|
|
|
|
|
/* Damn it, they didn't even put LPCSTR for the firs parameter!!! */
|
2005-10-23 18:24:30 +02:00
|
|
|
#ifdef UNICODE
|
|
|
|
wchar_t *workaround_for_ill_designed_api =
|
|
|
|
malloc( MAX_PATH * sizeof(wchar_t) );
|
|
|
|
mbstowcs( workaround_for_ill_designed_api, src, MAX_PATH );
|
|
|
|
workaround_for_ill_designed_api[MAX_PATH-1] = 0;
|
|
|
|
#else
|
2005-07-28 17:31:10 +02:00
|
|
|
char *workaround_for_ill_designed_api = strdup( src );
|
2005-10-23 18:24:30 +02:00
|
|
|
#endif
|
|
|
|
|
2005-07-28 17:31:10 +02:00
|
|
|
if( !WSAStringToAddress( workaround_for_ill_designed_api, af, NULL,
|
|
|
|
(LPSOCKADDR)&addr, &len ) )
|
|
|
|
{
|
|
|
|
free( workaround_for_ill_designed_api );
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
free( workaround_for_ill_designed_api );
|
|
|
|
|
|
|
|
switch( af )
|
|
|
|
{
|
|
|
|
case AF_INET6:
|
|
|
|
memcpy( dst, &((struct sockaddr_in6 *)&addr)->sin6_addr, 16 );
|
|
|
|
break;
|
2005-12-04 18:40:59 +01:00
|
|
|
|
2005-07-28 17:31:10 +02:00
|
|
|
case AF_INET:
|
|
|
|
memcpy( dst, &((struct sockaddr_in *)&addr)->sin_addr, 4 );
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
WSASetLastError( WSAEAFNOSUPPORT );
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
# else
|
|
|
|
/* Assume IPv6 is not supported. */
|
|
|
|
/* Would be safer and more simpler to use inet_aton() but it is most
|
|
|
|
* likely not provided either. */
|
|
|
|
uint32_t ipv4;
|
|
|
|
|
|
|
|
if( af != AF_INET )
|
|
|
|
{
|
|
|
|
errno = EAFNOSUPPORT;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ipv4 = inet_addr( src );
|
|
|
|
if( ipv4 == INADDR_NONE )
|
|
|
|
return -1;
|
|
|
|
|
2005-08-14 15:15:38 +02:00
|
|
|
memcpy( dst, &ipv4, 4 );
|
2005-07-28 17:31:10 +02:00
|
|
|
# endif /* WIN32 */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* HAVE_INET_PTON */
|