qse/qse/lib/cmn/nwio.c

1245 lines
26 KiB
C
Raw Normal View History

2012-04-27 14:33:14 +00:00
/*
* $Id$
*
Copyright 2006-2012 Chung, Hyung-Hwan.
2012-04-27 14:33:14 +00:00
This file is part of QSE.
QSE is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
2012-04-29 15:26:44 +00:00
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
2012-04-27 14:33:14 +00:00
QSE 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with QSE. If not, see <http://www.gnu.org/licenses/>.
*/
#include <qse/cmn/nwio.h>
#include <qse/cmn/time.h>
2012-04-27 14:33:14 +00:00
#include "mem.h"
#if defined(_WIN32)
2012-04-30 09:46:58 +00:00
# include <winsock2.h>
# include <ws2tcpip.h> /* sockaddr_in6 */
# include <windows.h>
2012-12-18 08:12:15 +00:00
# define USE_SELECT
2012-04-27 14:33:14 +00:00
#elif defined(__OS2__)
2013-10-21 08:39:53 +00:00
# if defined(TCPV40HDRS)
# define BSD_SELECT
# endif
2012-04-30 15:15:41 +00:00
# include <types.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <sys/ioctl.h>
2012-04-30 15:15:41 +00:00
# include <nerrno.h>
2012-12-18 08:12:15 +00:00
# if defined(TCPV40HDRS)
# define USE_SELECT
# include <sys/select.h>
# else
# include <unistd.h>
# endif
2012-04-27 14:33:14 +00:00
#elif defined(__DOS__)
2012-05-01 01:36:18 +00:00
/* TODO: consider watt-32 */
2012-04-27 14:33:14 +00:00
#else
# include "syscall.h"
# include <sys/socket.h>
# include <netinet/in.h>
# include <sys/time.h>
2012-12-18 08:12:15 +00:00
# define USE_SELECT
2012-04-27 14:33:14 +00:00
#endif
2012-04-29 15:26:44 +00:00
enum
{
STATUS_UDP_CONNECT = (1 << 0),
STATUS_TMOUT_R_PRESET = (1 << 1),
STATUS_TMOUT_W_PRESET = (1 << 2)
2012-04-29 15:26:44 +00:00
};
2012-05-01 01:36:18 +00:00
static qse_ssize_t socket_output (
qse_tio_t* tio, qse_tio_cmd_t cmd, void* buf, qse_size_t size);
static qse_ssize_t socket_input (
qse_tio_t* tio, qse_tio_cmd_t cmd, void* buf, qse_size_t size);
#define TMOUT_ENABLED(tmout) (tmout.sec >= 0 && tmout.nsec >= 0)
2012-04-27 14:33:14 +00:00
#if defined(_WIN32)
static qse_nwio_errnum_t skerr_to_errnum (DWORD e)
2012-04-27 14:33:14 +00:00
{
switch (e)
{
2012-04-30 09:46:58 +00:00
case WSA_NOT_ENOUGH_MEMORY:
2012-04-27 14:33:14 +00:00
return QSE_NWIO_ENOMEM;
2012-04-30 09:46:58 +00:00
case WSA_INVALID_PARAMETER:
case WSA_INVALID_HANDLE:
2012-04-27 14:33:14 +00:00
return QSE_NWIO_EINVAL;
2012-04-30 09:46:58 +00:00
case WSAEACCES:
2012-04-27 14:33:14 +00:00
return QSE_NWIO_EACCES;
2012-04-30 09:46:58 +00:00
case WSAEINTR:
return QSE_NWIO_EINTR;
2012-04-27 14:33:14 +00:00
2012-04-30 09:46:58 +00:00
case WSAECONNREFUSED:
case WSAENETUNREACH:
case WSAEHOSTUNREACH:
case WSAEHOSTDOWN:
return QSE_NWIO_ECONN;
2012-04-27 14:33:14 +00:00
default:
return QSE_NWIO_ESYSERR;
}
}
#elif defined(__OS2__)
static qse_nwio_errnum_t skerr_to_errnum (int e)
2012-04-27 14:33:14 +00:00
{
switch (e)
{
2012-12-18 08:12:15 +00:00
#if defined(SOCENOMEM)
2012-04-30 15:15:41 +00:00
case SOCENOMEM:
2012-04-27 14:33:14 +00:00
return QSE_NWIO_ENOMEM;
2012-12-18 08:12:15 +00:00
#endif
2012-04-27 14:33:14 +00:00
2012-04-30 15:15:41 +00:00
case SOCEINVAL:
2012-04-27 14:33:14 +00:00
return QSE_NWIO_EINVAL;
2012-04-30 15:15:41 +00:00
case SOCEACCES:
2012-04-27 14:33:14 +00:00
return QSE_NWIO_EACCES;
2012-12-18 08:12:15 +00:00
#if defined(SOCENOENT)
2012-04-30 15:15:41 +00:00
case SOCENOENT:
2012-04-27 14:33:14 +00:00
return QSE_NWIO_ENOENT;
2012-12-18 08:12:15 +00:00
#endif
2012-04-27 14:33:14 +00:00
2012-12-18 08:12:15 +00:00
#if defined(SOCEXIST)
2012-04-30 15:15:41 +00:00
case SOCEEXIST:
2012-04-27 14:33:14 +00:00
return QSE_NWIO_EEXIST;
2012-12-18 08:12:15 +00:00
#endif
2012-04-30 15:15:41 +00:00
case SOCEINTR:
return QSE_NWIO_EINTR;
2012-04-27 14:33:14 +00:00
2012-04-30 15:15:41 +00:00
case SOCEPIPE:
2012-04-27 14:33:14 +00:00
return QSE_NWIO_EPIPE;
2012-04-30 15:15:41 +00:00
case SOCECONNREFUSED:
case SOCENETUNREACH:
case SOCEHOSTUNREACH:
case SOCEHOSTDOWN:
return QSE_NWIO_ECONN;
2012-04-27 14:33:14 +00:00
default:
return QSE_NWIO_ESYSERR;
}
}
2012-04-30 15:15:41 +00:00
2012-04-27 14:33:14 +00:00
#elif defined(__DOS__)
static qse_nwio_errnum_t skerr_to_errnum (int e)
2012-04-27 14:33:14 +00:00
{
2012-05-01 01:36:18 +00:00
/* TODO: */
return QSE_NWIO_ESYSERR;
2012-04-27 14:33:14 +00:00
}
#else
static qse_nwio_errnum_t skerr_to_errnum (int e)
2012-04-27 14:33:14 +00:00
{
switch (e)
{
case ENOMEM:
return QSE_NWIO_ENOMEM;
case EINVAL:
return QSE_NWIO_EINVAL;
case EACCES:
return QSE_NWIO_EACCES;
case ENOENT:
return QSE_NWIO_ENOENT;
case EEXIST:
return QSE_NWIO_EEXIST;
case EINTR:
return QSE_NWIO_EINTR;
case EPIPE:
return QSE_NWIO_EPIPE;
case EAGAIN:
return QSE_NWIO_EAGAIN;
2012-04-30 09:46:58 +00:00
#if defined(ECONNREFUSED) || defined(ENETUNREACH) || defined(EHOSTUNREACH) || defined(EHOSTDOWN)
2012-04-27 14:33:14 +00:00
#if defined(ECONNREFUSED)
case ECONNREFUSED:
#endif
#if defined(ENETUNREACH)
case ENETUNREACH:
2012-04-30 09:46:58 +00:00
#endif
#if defined(EHOSTUNREACH)
case EHOSTUNREACH:
#endif
#if defined(EHOSTDOWN)
case EHOSTDOWN:
2012-04-27 14:33:14 +00:00
#endif
return QSE_NWIO_ECONN;
#endif
default:
return QSE_NWIO_ESYSERR;
}
}
#endif
static qse_nwio_errnum_t tio_errnum_to_nwio_errnum (qse_tio_t* tio)
{
switch (tio->errnum)
{
case QSE_TIO_ENOMEM:
return QSE_NWIO_ENOMEM;
case QSE_TIO_EINVAL:
return QSE_NWIO_EINVAL;
case QSE_TIO_EACCES:
return QSE_NWIO_EACCES;
case QSE_TIO_ENOENT:
return QSE_NWIO_ENOENT;
case QSE_TIO_EILSEQ:
return QSE_NWIO_EILSEQ;
case QSE_TIO_EICSEQ:
return QSE_NWIO_EICSEQ;
case QSE_TIO_EILCHR:
return QSE_NWIO_EILCHR;
default:
return QSE_NWIO_EOTHER;
}
}
static int wait_for_data (qse_nwio_t* nwio, const qse_ntime_t* tmout, int what)
2012-07-28 14:43:59 +00:00
{
int xret;
2012-08-01 11:25:53 +00:00
2012-12-18 08:12:15 +00:00
#if defined(USE_SELECT)
2012-07-28 14:43:59 +00:00
fd_set fds[2];
struct timeval tv;
FD_ZERO (&fds[0]);
FD_ZERO (&fds[1]);
FD_SET (nwio->handle, &fds[what]);
tv.tv_sec = tmout->sec;
tv.tv_usec = QSE_NSEC_TO_USEC (tmout->nsec);
2012-07-28 14:43:59 +00:00
xret = select (nwio->handle + 1, &fds[0], &fds[1], QSE_NULL, &tv);
2012-12-18 08:12:15 +00:00
#if defined(_WIN32)
if (xret == SOCKET_ERROR)
2012-12-18 08:12:15 +00:00
#else
if (xret <= -1)
#endif
{
2012-12-18 08:12:15 +00:00
#if defined(_WIN32)
nwio->errnum = skerr_to_errnum (WSAGetLastError());
2012-12-18 08:12:15 +00:00
#elif defined(__OS2__)
nwio->errnum = skerr_to_errnum (sock_errno());
2012-12-18 08:12:15 +00:00
#else
nwio->errnum = skerr_to_errnum (errno);
2012-12-18 08:12:15 +00:00
#endif
return -1;
}
2012-08-01 11:25:53 +00:00
else if (xret == 0)
{
nwio->errnum = QSE_NWIO_ETMOUT;
return -1;
}
2012-12-18 08:12:15 +00:00
return 0;
#elif defined(__OS2__)
2012-08-01 11:25:53 +00:00
int count[2] = { 0, 0 };
long tmout_msecs;
2012-08-01 11:25:53 +00:00
count[what]++;
tmout_msecs = QSE_SECNSEC_TO_MSEC (tmout->sec, tmout->nsec);
xret = os2_select (&nwio->handle, count[0], count[1], 0, tmout_msecs);
if (xret <= -1)
2012-08-01 11:25:53 +00:00
{
nwio->errnum = skerr_to_errnum (sock_errno());
2012-08-01 11:25:53 +00:00
return -1;
}
else if (xret == 0)
{
nwio->errnum = QSE_NWIO_ETMOUT;
return -1;
}
2012-12-18 08:12:15 +00:00
return 0;
2012-08-01 11:25:53 +00:00
2012-12-18 08:12:15 +00:00
#else
2012-08-01 11:25:53 +00:00
nwio->errnum = QSE_NWIO_ENOIMPL;
return -1;
2012-08-01 11:25:53 +00:00
#endif
2012-07-28 14:43:59 +00:00
}
2012-04-27 14:33:14 +00:00
qse_nwio_t* qse_nwio_open (
qse_mmgr_t* mmgr, qse_size_t xtnsize, const qse_nwad_t* nwad,
int flags, const qse_nwio_tmout_t* tmout)
2012-04-27 14:33:14 +00:00
{
qse_nwio_t* nwio;
nwio = QSE_MMGR_ALLOC (mmgr, QSE_SIZEOF(qse_nwio_t) + xtnsize);
if (nwio == QSE_NULL) return QSE_NULL;
if (qse_nwio_init (nwio, mmgr, nwad, flags, tmout) <= -1)
2012-04-27 14:33:14 +00:00
{
QSE_MMGR_FREE (mmgr, nwio);
return QSE_NULL;
}
QSE_MEMSET (nwio + 1, 0, xtnsize);
2012-04-27 14:33:14 +00:00
return nwio;
}
void qse_nwio_close (qse_nwio_t* nwio)
{
qse_nwio_fini (nwio);
QSE_MMGR_FREE (nwio->mmgr, nwio);
}
static int preset_tmout (qse_nwio_t* nwio)
{
#if defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO)
#if defined(_WIN32)
DWORD tv;
#elif defined(__OS2__)
long tv;
#else
struct timeval tv;
#endif
if (TMOUT_ENABLED(nwio->tmout.r))
{
#if defined(_WIN32) || defined(__OS2__)
tv = QSE_SEC_TO_MSEC(nwio->tmout.r.sec) + QSE_NSEC_TO_MSEC (nwio->tmout.r.nsec);
#else
tv.tv_sec = nwio->tmout.r.sec;
tv.tv_usec = QSE_NSEC_TO_USEC (nwio->tmout.r.nsec);
#endif
if (setsockopt (nwio->handle, SOL_SOCKET, SO_RCVTIMEO, (void*)&tv, QSE_SIZEOF(tv)) <= -1)
{
#if defined(_WIN32)
nwio->errnum = skerr_to_errnum (WSAGetLastError());
#elif defined(__OS2__)
nwio->errnum = skerr_to_errnum (sock_errno());
#else
nwio->errnum = skerr_to_errnum (errno);
#endif
return -1; /* tried to set but failed */
}
nwio->status |= STATUS_TMOUT_R_PRESET;
}
if (TMOUT_ENABLED(nwio->tmout.w))
{
#if defined(_WIN32) || defined(__OS2__)
tv = QSE_SEC_TO_MSEC(nwio->tmout.w.sec) + QSE_NSEC_TO_MSEC (nwio->tmout.w.nsec);
#else
tv.tv_sec = nwio->tmout.w.sec;
tv.tv_usec = QSE_NSEC_TO_USEC (nwio->tmout.w.nsec);
#endif
if (setsockopt (nwio->handle, SOL_SOCKET, SO_SNDTIMEO, (void*)&tv, QSE_SIZEOF(tv)) <= -1)
{
#if defined(_WIN32)
nwio->errnum = skerr_to_errnum (WSAGetLastError());
#elif defined(__OS2__)
nwio->errnum = skerr_to_errnum (sock_errno());
#else
nwio->errnum = skerr_to_errnum (errno);
#endif
return -1; /* tried to set but failed */
}
nwio->status |= STATUS_TMOUT_W_PRESET;
}
return 1; /* set successfully - don't need a multiplexer */
#endif
return 0; /* no measn to set it */
}
2012-04-27 14:33:14 +00:00
int qse_nwio_init (
qse_nwio_t* nwio, qse_mmgr_t* mmgr, const qse_nwad_t* nwad,
int flags, const qse_nwio_tmout_t* tmout)
2012-04-27 14:33:14 +00:00
{
qse_skad_t addr;
2012-05-01 01:36:18 +00:00
#if defined(HAVE_SOCKLEN_T)
2012-04-29 15:26:44 +00:00
socklen_t addrlen;
#else
2012-04-27 14:33:14 +00:00
int addrlen;
2012-04-29 15:26:44 +00:00
#endif
int family, type, tmp;
2012-04-27 14:33:14 +00:00
QSE_MEMSET (nwio, 0, QSE_SIZEOF(*nwio));
nwio->mmgr = mmgr;
nwio->flags = flags;
2012-04-30 15:15:41 +00:00
nwio->errnum = QSE_NWIO_ENOERR;
if (tmout) nwio->tmout = *tmout;
else
{
nwio->tmout.r.sec = -1;
nwio->tmout.w.sec = -1;
nwio->tmout.c.sec = -1;
nwio->tmout.a.sec = -1;
}
tmp = qse_nwadtoskad (nwad, &addr);
2012-04-30 10:25:19 +00:00
if (tmp <= -1)
{
nwio->errnum = QSE_NWIO_EINVAL;
return -1;
}
addrlen = tmp;
2012-04-27 14:33:14 +00:00
#if defined(SOCK_STREAM) && defined(SOCK_DGRAM)
2012-04-29 15:26:44 +00:00
if (flags & QSE_NWIO_TCP) type = SOCK_STREAM;
else if (flags & QSE_NWIO_UDP) type = SOCK_DGRAM;
else
2012-05-01 01:36:18 +00:00
#endif
2012-04-29 15:26:44 +00:00
{
nwio->errnum = QSE_NWIO_EINVAL;
return -1;
}
family = qse_skadfamily (&addr);
2012-04-30 09:46:58 +00:00
#if defined(_WIN32)
nwio->handle = socket (family, type, 0);
if (nwio->handle == INVALID_SOCKET)
{
nwio->errnum = skerr_to_errnum (WSAGetLastError());
2012-04-30 09:46:58 +00:00
goto oops;
}
if ((flags & QSE_NWIO_TCP) && (flags & QSE_NWIO_KEEPALIVE))
{
int optval = 1;
setsockopt (nwio->handle, SOL_SOCKET, SO_KEEPALIVE, (void*)&optval, QSE_SIZEOF(optval));
}
2012-04-30 09:46:58 +00:00
if (flags & QSE_NWIO_PASSIVE)
{
qse_nwio_hnd_t handle;
if (flags & QSE_NWIO_REUSEADDR)
2012-05-01 15:02:31 +00:00
{
int optval = 1;
setsockopt (nwio->handle, SOL_SOCKET, SO_REUSEADDR, (void*)&optval, QSE_SIZEOF(optval));
}
2012-04-30 09:46:58 +00:00
if (bind (nwio->handle, (struct sockaddr*)&addr, addrlen) == SOCKET_ERROR)
{
nwio->errnum = skerr_to_errnum (WSAGetLastError());
2012-04-30 09:46:58 +00:00
goto oops;
}
if (flags & QSE_NWIO_TCP)
{
if (listen (nwio->handle, 10) == SOCKET_ERROR)
{
nwio->errnum = skerr_to_errnum (WSAGetLastError());
2012-04-30 09:46:58 +00:00
goto oops;
}
if (TMOUT_ENABLED(nwio->tmout.a) &&
wait_for_data (nwio, &nwio->tmout.a, 0) <= -1) goto oops;
2012-04-30 09:46:58 +00:00
handle = accept (nwio->handle, (struct sockaddr*)&addr, &addrlen);
if (handle == INVALID_SOCKET)
{
nwio->errnum = skerr_to_errnum (WSAGetLastError());
2012-04-30 09:46:58 +00:00
goto oops;
}
closesocket (nwio->handle);
nwio->handle = handle;
}
else if (flags & QSE_NWIO_UDP)
{
2012-04-30 15:15:41 +00:00
nwio->status |= STATUS_UDP_CONNECT;
2012-04-30 09:46:58 +00:00
}
}
else
{
int xret;
if (TMOUT_ENABLED(nwio->tmout.c) && (flags & QSE_NWIO_TCP))
2012-04-30 09:46:58 +00:00
{
unsigned long cmd = 1;
if (ioctlsocket(nwio->handle, FIONBIO, &cmd) == SOCKET_ERROR)
{
nwio->errnum = skerr_to_errnum (WSAGetLastError());
goto oops;
}
}
xret = connect (nwio->handle, (struct sockaddr*)&addr, addrlen);
if (TMOUT_ENABLED(nwio->tmout.c) && (flags & QSE_NWIO_TCP))
{
unsigned long cmd = 0;
if ((xret == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) ||
ioctlsocket (nwio->handle, FIONBIO, &cmd) == SOCKET_ERROR)
{
nwio->errnum = skerr_to_errnum (WSAGetLastError());
goto oops;
}
if (wait_for_data (nwio, &nwio->tmout.c, 1) <= -1) goto oops;
else
{
int xlen;
DWORD xerr;
xlen = QSE_SIZEOF(xerr);
if (getsockopt (nwio->handle, SOL_SOCKET, SO_ERROR, (char*)&xerr, &xlen) == SOCKET_ERROR)
{
nwio->errnum = skerr_to_errnum (WSAGetLastError());
goto oops;
}
else if (xerr != 0)
{
nwio->errnum = skerr_to_errnum (xerr);
goto oops;
}
}
}
else
{
if (xret == SOCKET_ERROR)
{
nwio->errnum = skerr_to_errnum (WSAGetLastError());
goto oops;
}
2012-04-30 09:46:58 +00:00
}
}
#elif defined(__OS2__)
2012-04-30 15:15:41 +00:00
nwio->handle = socket (family, type, 0);
if (nwio->handle <= -1)
{
nwio->errnum = skerr_to_errnum (sock_errno());
2012-04-30 15:15:41 +00:00
goto oops;
}
if ((flags & QSE_NWIO_TCP) && (flags & QSE_NWIO_KEEPALIVE))
{
int optval = 1;
setsockopt (nwio->handle, SOL_SOCKET, SO_KEEPALIVE, (void*)&optval, QSE_SIZEOF(optval));
}
2012-04-30 15:15:41 +00:00
if (flags & QSE_NWIO_PASSIVE)
{
qse_nwio_hnd_t handle;
if (flags & QSE_NWIO_REUSEADDR)
2012-05-01 15:02:31 +00:00
{
int optval = 1;
setsockopt (nwio->handle, SOL_SOCKET, SO_REUSEADDR, (void*)&optval, QSE_SIZEOF(optval));
}
2012-04-30 15:15:41 +00:00
if (bind (nwio->handle, (struct sockaddr*)&addr, addrlen) <= -1)
{
nwio->errnum = skerr_to_errnum (sock_errno());
2012-04-30 15:15:41 +00:00
goto oops;
}
if (flags & QSE_NWIO_TCP)
{
if (listen (nwio->handle, 10) <= -1)
{
nwio->errnum = skerr_to_errnum (sock_errno());
2012-04-30 15:15:41 +00:00
goto oops;
}
if (TMOUT_ENABLED(nwio->tmout.a) &&
wait_for_data (nwio, &nwio->tmout.a, 0) <= -1) goto oops;
2012-08-01 11:25:53 +00:00
2012-04-30 15:15:41 +00:00
handle = accept (nwio->handle, (struct sockaddr*)&addr, &addrlen);
if (handle <= -1)
{
nwio->errnum = skerr_to_errnum (sock_errno());
2012-04-30 15:15:41 +00:00
goto oops;
}
soclose (nwio->handle);
nwio->handle = handle;
}
else if (flags & QSE_NWIO_UDP)
{
nwio->status |= STATUS_UDP_CONNECT;
}
}
else
{
int xret;
if (TMOUT_ENABLED(nwio->tmout.c) && (flags & QSE_NWIO_TCP))
2012-04-30 15:15:41 +00:00
{
int noblk = 1;
2012-12-18 08:12:15 +00:00
if (ioctl (nwio->handle, FIONBIO, (void*)&noblk, QSE_SIZEOF(noblk)) <= -1)
{
nwio->errnum = skerr_to_errnum (sock_errno());
goto oops;
}
}
xret = connect (nwio->handle, (struct sockaddr*)&addr, addrlen);
if (TMOUT_ENABLED(nwio->tmout.c) && (flags & QSE_NWIO_TCP))
{
int noblk = 0;
if ((xret <= -1 && sock_errno() != SOCEINPROGRESS) ||
2012-12-18 08:12:15 +00:00
ioctl (nwio->handle, FIONBIO, (void*)&noblk, QSE_SIZEOF(noblk)) <= -1)
{
nwio->errnum = skerr_to_errnum (sock_errno());
goto oops;
}
if (wait_for_data (nwio, &nwio->tmout.c, 1) <= -1) goto oops;
else
{
int xlen, xerr;
xlen = QSE_SIZEOF(xerr);
if (getsockopt (nwio->handle, SOL_SOCKET, SO_ERROR, (char*)&xerr, &xlen) <= -1)
{
nwio->errnum = skerr_to_errnum (sock_errno());
goto oops;
}
else if (xerr != 0)
{
nwio->errnum = skerr_to_errnum (xerr);
goto oops;
}
}
}
else
{
if (xret <= -1)
{
nwio->errnum = skerr_to_errnum (sock_errno());
goto oops;
}
2012-04-30 15:15:41 +00:00
}
}
2012-04-30 09:46:58 +00:00
#elif defined(__DOS__)
nwio->errnum = QSE_NWIO_ENOIMPL;
return -1;
#else
2012-07-02 14:21:40 +00:00
#if defined(SOCK_CLOEXEC)
nwio->handle = socket (family, type | SOCK_CLOEXEC, 0);
#else
2012-04-29 15:26:44 +00:00
nwio->handle = socket (family, type, 0);
2012-07-02 14:21:40 +00:00
#endif
2012-04-27 14:33:14 +00:00
if (nwio->handle <= -1)
{
nwio->errnum = skerr_to_errnum (errno);
2012-04-27 14:33:14 +00:00
goto oops;
}
2012-07-02 14:21:40 +00:00
#if !defined(SOCK_CLOEXEC) && defined(FD_CLOEXEC)
2012-04-27 14:33:14 +00:00
{
int tmp = fcntl (nwio->handle, F_GETFD);
if (tmp >= 0) fcntl (nwio->handle, F_SETFD, tmp | FD_CLOEXEC);
}
2012-04-30 09:46:58 +00:00
#endif
2012-04-27 14:33:14 +00:00
if ((flags & QSE_NWIO_TCP) && (flags & QSE_NWIO_KEEPALIVE))
{
int optval = 1;
setsockopt (nwio->handle, SOL_SOCKET, SO_KEEPALIVE, (void*)&optval, QSE_SIZEOF(optval));
}
2012-04-29 15:26:44 +00:00
if (flags & QSE_NWIO_PASSIVE)
2012-04-27 14:33:14 +00:00
{
qse_nwio_hnd_t handle;
2012-05-01 15:02:31 +00:00
#if defined(SO_REUSEADDR)
if (flags & QSE_NWIO_REUSEADDR)
2012-05-01 15:02:31 +00:00
{
int optval = 1;
setsockopt (nwio->handle, SOL_SOCKET, SO_REUSEADDR, (void*)&optval, QSE_SIZEOF(optval));
}
#endif
2012-04-29 15:26:44 +00:00
if (bind (nwio->handle, (struct sockaddr*)&addr, addrlen) <= -1)
2012-04-27 14:33:14 +00:00
{
nwio->errnum = skerr_to_errnum (errno);
2012-04-27 14:33:14 +00:00
goto oops;
}
2012-04-29 15:26:44 +00:00
if (flags & QSE_NWIO_TCP)
2012-04-27 14:33:14 +00:00
{
2012-04-29 15:26:44 +00:00
if (listen (nwio->handle, 10) <= -1)
{
nwio->errnum = skerr_to_errnum (errno);
2012-04-29 15:26:44 +00:00
goto oops;
}
if (TMOUT_ENABLED(nwio->tmout.a) &&
wait_for_data (nwio, &nwio->tmout.a, 0) <= -1) goto oops;
2012-07-28 14:43:59 +00:00
2012-04-29 15:26:44 +00:00
handle = accept (nwio->handle, (struct sockaddr*)&addr, &addrlen);
if (handle <= -1)
{
nwio->errnum = skerr_to_errnum (errno);
2012-04-29 15:26:44 +00:00
goto oops;
}
QSE_CLOSE (nwio->handle);
nwio->handle = handle;
}
else if (flags & QSE_NWIO_UDP)
{
2012-04-30 15:15:41 +00:00
nwio->status |= STATUS_UDP_CONNECT;
2012-04-27 14:33:14 +00:00
}
}
else
{
int xret;
if (TMOUT_ENABLED(nwio->tmout.c) && (flags & QSE_NWIO_TCP))
2012-04-27 14:33:14 +00:00
{
int orgfl;
orgfl = fcntl (nwio->handle, F_GETFL, 0);
if (orgfl <= -1 ||
fcntl (nwio->handle, F_SETFL, orgfl | O_NONBLOCK) <= -1)
{
nwio->errnum = skerr_to_errnum (errno);
goto oops;
}
xret = connect (nwio->handle, (struct sockaddr*)&addr, addrlen);
if ((xret <= -1 && errno != EINPROGRESS) ||
fcntl (nwio->handle, F_SETFL, orgfl) <= -1)
{
nwio->errnum = skerr_to_errnum (errno);
goto oops;
}
if (wait_for_data (nwio, &nwio->tmout.c, 1) <= -1) goto oops;
else
{
#if defined(HAVE_SOCKLEN_T)
socklen_t xlen;
#else
int xlen;
#endif
2012-07-28 14:43:59 +00:00
xlen = QSE_SIZEOF(xret);
if (getsockopt (nwio->handle, SOL_SOCKET, SO_ERROR, (char*)&xret, &xlen) <= -1)
{
nwio->errnum = skerr_to_errnum (errno);
goto oops;
}
else if (xret != 0)
{
nwio->errnum = skerr_to_errnum (xret);
goto oops;
}
}
}
else
{
xret = connect (nwio->handle, (struct sockaddr*)&addr, addrlen);
if (xret <= -1)
{
nwio->errnum = skerr_to_errnum (errno);
goto oops;
}
2012-04-27 14:33:14 +00:00
}
}
#endif
if (flags & QSE_NWIO_TEXT)
{
int topt = 0;
if (flags & QSE_NWIO_IGNOREMBWCERR) topt |= QSE_TIO_IGNOREMBWCERR;
if (flags & QSE_NWIO_NOAUTOFLUSH) topt |= QSE_TIO_NOAUTOFLUSH;
nwio->tio = qse_tio_open (mmgr, QSE_SIZEOF(qse_nwio_t*), topt);
if (nwio->tio == QSE_NULL)
{
nwio->errnum = QSE_NWIO_ENOMEM;
goto oops;
}
/* store the back-reference to nwio in the extension area.*/
*(qse_nwio_t**)QSE_XTN(nwio->tio) = nwio;
if (qse_tio_attachin (nwio->tio, socket_input, QSE_NULL, 4096) <= -1 ||
qse_tio_attachout (nwio->tio, socket_output, QSE_NULL, 4096) <= -1)
{
if (nwio->errnum == QSE_NWIO_ENOERR)
nwio->errnum = tio_errnum_to_nwio_errnum (nwio->tio);
goto oops;
}
}
preset_tmout (nwio);
2012-04-27 14:33:14 +00:00
return 0;
oops:
2012-04-30 09:46:58 +00:00
if (nwio->tio)
{
qse_tio_close (nwio->tio);
nwio->tio = QSE_NULL;
}
2012-04-27 14:33:14 +00:00
#if defined(_WIN32)
2012-04-30 09:46:58 +00:00
if (nwio->handle != INVALID_SOCKET) closesocket (nwio->handle);
2012-04-27 14:33:14 +00:00
#elif defined(__OS2__)
2012-04-30 15:15:41 +00:00
if (nwio->handle >= 0) soclose (nwio->handle);
2012-04-30 09:46:58 +00:00
2012-04-27 14:33:14 +00:00
#elif defined(__DOS__)
2012-04-30 15:15:41 +00:00
/* TODO: */
2012-04-27 14:33:14 +00:00
#else
2012-04-30 09:46:58 +00:00
if (nwio->handle >= 0) QSE_CLOSE (nwio->handle);
2012-04-27 14:33:14 +00:00
#endif
return -1;
}
void qse_nwio_fini (qse_nwio_t* nwio)
{
/*if (qse_nwio_flush (nwio) <= -1) return -1;*/
qse_nwio_flush (nwio);
if (nwio->tio)
{
qse_tio_close (nwio->tio);
nwio->tio = QSE_NULL;
}
2012-04-30 09:46:58 +00:00
#if defined(_WIN32)
closesocket (nwio->handle);
#elif defined(__OS2__)
/* TODO: */
#elif defined(__DOS__)
/* TODO: */
#else
QSE_CLOSE (nwio->handle);
#endif
2012-04-27 14:33:14 +00:00
}
qse_mmgr_t* qse_nwio_getmmgr (qse_nwio_t* nwio)
{
return nwio->mmgr;
}
void* qse_nwio_getxtn (qse_nwio_t* nwio)
{
return QSE_XTN (nwio);
}
2012-04-27 14:33:14 +00:00
qse_nwio_errnum_t qse_nwio_geterrnum (const qse_nwio_t* nwio)
{
return nwio->errnum;
}
qse_cmgr_t* qse_nwio_getcmgr (qse_nwio_t* nwio)
{
return nwio->tio? qse_tio_getcmgr (nwio->tio): QSE_NULL;
}
void qse_nwio_setcmgr (qse_nwio_t* nwio, qse_cmgr_t* cmgr)
{
if (nwio->tio) qse_tio_setcmgr (nwio->tio, cmgr);
}
qse_nwio_hnd_t qse_nwio_gethandle (const qse_nwio_t* nwio)
{
2012-04-30 09:46:58 +00:00
return nwio->handle;
2012-04-27 14:33:14 +00:00
}
qse_ubi_t qse_nwio_gethandleasubi (const qse_nwio_t* nwio)
{
qse_ubi_t ubi;
#if defined(_WIN32)
2012-04-30 09:46:58 +00:00
ubi.intptr = nwio->handle;
2012-04-27 14:33:14 +00:00
#elif defined(__OS2__)
2012-04-30 15:15:41 +00:00
ubi.i = nwio->handle;
2012-04-27 14:33:14 +00:00
#elif defined(__DOS__)
2012-04-30 15:15:41 +00:00
ubi.i = nwio->handle;
2012-04-27 14:33:14 +00:00
#else
ubi.i = nwio->handle;
#endif
return ubi;
}
qse_ssize_t qse_nwio_flush (qse_nwio_t* nwio)
{
qse_ssize_t n;
if (nwio->tio)
{
nwio->errnum = QSE_NWIO_ENOERR;
n = qse_tio_flush (nwio->tio);
if (n <= -1 && nwio->errnum == QSE_NWIO_ENOERR)
nwio->errnum = tio_errnum_to_nwio_errnum (nwio->tio);
}
else n = 0;
return n;
}
void qse_nwio_purge (qse_nwio_t* nwio)
{
if (nwio->tio) qse_tio_purge (nwio->tio);
}
/* ---------------------------------------------------------- */
static qse_ssize_t nwio_read (qse_nwio_t* nwio, void* buf, qse_size_t size)
{
#if defined(_WIN32)
2012-04-30 09:46:58 +00:00
int count;
2012-04-27 14:33:14 +00:00
#elif defined(__OS2__)
2012-04-30 15:15:41 +00:00
int n;
2012-04-27 14:33:14 +00:00
#elif defined(__DOS__)
int n;
#else
qse_ssize_t n;
#endif
#if defined(_WIN32)
2012-04-30 09:46:58 +00:00
if (size > (QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(int)))
size = QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(int);
2012-04-30 15:15:41 +00:00
if (nwio->status & STATUS_UDP_CONNECT)
2012-04-30 09:46:58 +00:00
{
qse_skad_t addr;
2012-04-30 09:46:58 +00:00
int addrlen;
addrlen = QSE_SIZEOF(addr);
if (TMOUT_ENABLED(nwio->tmout.a) &&
wait_for_data (nwio, &nwio->tmout.a, 0) <= -1) return -1;
2012-04-30 09:46:58 +00:00
count = recvfrom (
nwio->handle, buf, size, 0,
(struct sockaddr*)&addr, &addrlen);
2012-04-30 15:15:41 +00:00
if (count == SOCKET_ERROR)
{
nwio->errnum = skerr_to_errnum (WSAGetLastError());
2012-04-30 15:15:41 +00:00
}
2012-04-30 09:46:58 +00:00
else if (count >= 1)
{
/* for udp, it just creates a stream with the
* first sender */
if (connect (nwio->handle, (struct sockaddr*)&addr, addrlen) <= -1)
{
nwio->errnum = skerr_to_errnum (WSAGetLastError());
2012-04-30 09:46:58 +00:00
return -1;
}
2012-04-30 15:15:41 +00:00
nwio->status &= ~STATUS_UDP_CONNECT;
2012-04-30 09:46:58 +00:00
}
}
else
{
if (!(nwio->status & STATUS_TMOUT_R_PRESET) &&
TMOUT_ENABLED(nwio->tmout.r) &&
wait_for_data (nwio, &nwio->tmout.r, 0) <= -1) return -1;
2012-04-30 09:46:58 +00:00
count = recv (nwio->handle, buf, size, 0);
if (count == SOCKET_ERROR) nwio->errnum = skerr_to_errnum (WSAGetLastError());
2012-04-30 09:46:58 +00:00
}
return count;
2012-04-27 14:33:14 +00:00
#elif defined(__OS2__)
2012-04-30 15:15:41 +00:00
if (size > (QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(int)))
size = QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(int);
if (nwio->status & STATUS_UDP_CONNECT)
{
qse_skad_t addr;
2012-04-30 15:15:41 +00:00
int addrlen;
addrlen = QSE_SIZEOF(addr);
2012-08-01 11:25:53 +00:00
if (TMOUT_ENABLED(nwio->tmout.a) &&
wait_for_data (nwio, &nwio->tmout.a, 0) <= -1) return -1;
2012-08-01 11:25:53 +00:00
2012-04-30 15:15:41 +00:00
n = recvfrom (
nwio->handle, buf, size, 0,
(struct sockaddr*)&addr, &addrlen);
if (n <= -1)
{
nwio->errnum = skerr_to_errnum (sock_errno());
2012-04-30 15:15:41 +00:00
}
else if (n >= 1)
{
/* for udp, it just creates a stream with the
* first sender */
if (connect (nwio->handle, (struct sockaddr*)&addr, addrlen) <= -1)
{
nwio->errnum = skerr_to_errnum (sock_errno());
2012-04-30 15:15:41 +00:00
return -1;
}
nwio->status &= ~STATUS_UDP_CONNECT;
}
}
else
{
if (!(nwio->status & STATUS_TMOUT_R_PRESET) &&
TMOUT_ENABLED(nwio->tmout.r) &&
wait_for_data (nwio, &nwio->tmout.r, 0) <= -1) return -1;
2012-08-01 11:25:53 +00:00
2012-04-30 15:15:41 +00:00
n = recv (nwio->handle, buf, size, 0);
if (n <= -1) nwio->errnum = skerr_to_errnum (sock_errno());
2012-04-30 15:15:41 +00:00
}
return n;
2012-04-27 14:33:14 +00:00
#elif defined(__DOS__)
2012-04-30 09:46:58 +00:00
nwio->errnum = QSE_NWIO_ENOIMPL;
return -1;
2012-04-27 14:33:14 +00:00
#else
if (size > (QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(size_t)))
size = QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(size_t);
reread:
2012-04-30 15:15:41 +00:00
if (nwio->status & STATUS_UDP_CONNECT)
2012-04-27 14:33:14 +00:00
{
qse_skad_t addr;
2012-05-01 01:36:18 +00:00
#if defined(HAVE_SOCKLEN_T)
2012-04-29 15:26:44 +00:00
socklen_t addrlen;
#else
int addrlen;
#endif
addrlen = QSE_SIZEOF(addr);
2012-07-28 14:43:59 +00:00
/* it's similar to accept for tcp because i'm expecting
* the first sender and call connect() to it below just
* like the 'nc' utility does.
* so i treat this recvfrom() as if it is accept().
*/
if (TMOUT_ENABLED(nwio->tmout.a) &&
wait_for_data (nwio, &nwio->tmout.a, 0) <= -1) return -1;
2012-07-28 14:43:59 +00:00
2012-04-29 15:26:44 +00:00
n = recvfrom (
nwio->handle, buf, size, 0,
(struct sockaddr*)&addr, &addrlen);
if (n <= -1)
2012-04-27 14:33:14 +00:00
{
2012-04-29 15:26:44 +00:00
if (errno == EINTR)
{
if (nwio->flags & QSE_NWIO_READNORETRY)
nwio->errnum = QSE_NWIO_EINTR;
else goto reread;
}
else
{
nwio->errnum = skerr_to_errnum (errno);
2012-04-29 15:26:44 +00:00
}
2012-04-27 14:33:14 +00:00
}
2012-04-29 15:26:44 +00:00
else if (n >= 1)
2012-04-27 14:33:14 +00:00
{
2012-04-29 15:26:44 +00:00
/* for udp, it just creates a stream with the
* first sender */
if (connect (nwio->handle, (struct sockaddr*)&addr, addrlen) <= -1)
{
nwio->errnum = skerr_to_errnum (errno);
2012-04-29 15:26:44 +00:00
return -1;
}
2012-04-30 15:15:41 +00:00
nwio->status &= ~STATUS_UDP_CONNECT;
2012-04-29 15:26:44 +00:00
}
}
else
{
if (!(nwio->status & STATUS_TMOUT_R_PRESET) &&
TMOUT_ENABLED(nwio->tmout.r) &&
wait_for_data (nwio, &nwio->tmout.r, 0) <= -1) return -1;
2012-04-29 15:26:44 +00:00
n = recv (nwio->handle, buf, size, 0);
if (n <= -1)
{
if (errno == EINTR)
{
if (nwio->flags & QSE_NWIO_READNORETRY)
nwio->errnum = QSE_NWIO_EINTR;
else goto reread;
}
else
{
nwio->errnum = skerr_to_errnum (errno);
2012-04-29 15:26:44 +00:00
}
2012-04-27 14:33:14 +00:00
}
}
return n;
#endif
}
qse_ssize_t qse_nwio_read (qse_nwio_t* nwio, void* buf, qse_size_t size)
{
if (nwio->tio == QSE_NULL)
return nwio_read (nwio, buf, size);
else
{
qse_ssize_t n;
nwio->errnum = QSE_NWIO_ENOERR;
n = qse_tio_read (nwio->tio, buf, size);
if (n <= -1 && nwio->errnum == QSE_NWIO_ENOERR)
nwio->errnum = tio_errnum_to_nwio_errnum (nwio->tio);
return n;
}
}
static qse_ssize_t nwio_write (qse_nwio_t* nwio, const void* data, qse_size_t size)
{
#if defined(_WIN32)
2012-04-30 09:46:58 +00:00
int count;
2012-04-27 14:33:14 +00:00
#elif defined(__OS2__)
2012-04-30 15:15:41 +00:00
int n;
2012-04-27 14:33:14 +00:00
#elif defined(__DOS__)
int n;
#else
qse_ssize_t n;
#endif
#if defined(_WIN32)
2012-04-30 09:46:58 +00:00
if (size > (QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(int)))
size = QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(int);
if (!(nwio->status & STATUS_TMOUT_W_PRESET) &&
TMOUT_ENABLED(nwio->tmout.w) &&
wait_for_data (nwio, &nwio->tmout.w, 1) <= -1) return -1;
2012-04-30 09:46:58 +00:00
count = send (nwio->handle, data, size, 0);
if (count == SOCKET_ERROR) nwio->errnum = skerr_to_errnum (WSAGetLastError());
2012-04-30 09:46:58 +00:00
return count;
2012-04-27 14:33:14 +00:00
#elif defined(__OS2__)
2012-04-30 15:15:41 +00:00
if (size > (QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(int)))
size = QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(int);
if (!(nwio->status & STATUS_TMOUT_W_PRESET) &&
TMOUT_ENABLED(nwio->tmout.w) &&
wait_for_data (nwio, &nwio->tmout.w, 1) <= -1) return -1;
2012-08-01 11:25:53 +00:00
2012-04-30 15:15:41 +00:00
n = send (nwio->handle, data, size, 0);
if (n <= -1) nwio->errnum = skerr_to_errnum (sock_errno());
2012-04-30 15:15:41 +00:00
return n;
2012-04-27 14:33:14 +00:00
#elif defined(__DOS__)
2012-04-30 09:46:58 +00:00
nwio->errnum = QSE_NWIO_ENOIMPL;
return -1;
2012-04-27 14:33:14 +00:00
#else
if (size > (QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(size_t)))
size = QSE_TYPE_MAX(qse_ssize_t) & QSE_TYPE_MAX(size_t);
rewrite:
if (!(nwio->status & STATUS_TMOUT_W_PRESET) &&
TMOUT_ENABLED(nwio->tmout.w) &&
wait_for_data (nwio, &nwio->tmout.w, 1) <= -1) return -1;
2012-07-28 14:43:59 +00:00
2012-04-27 14:33:14 +00:00
n = send (nwio->handle, data, size, 0);
if (n <= -1)
{
if (errno == EINTR)
{
if (nwio->flags & QSE_NWIO_WRITENORETRY)
nwio->errnum = QSE_NWIO_EINTR;
else goto rewrite;
}
else
{
nwio->errnum = skerr_to_errnum (errno);
2012-04-27 14:33:14 +00:00
}
}
return n;
#endif
}
qse_ssize_t qse_nwio_write (qse_nwio_t* nwio, const void* data, qse_size_t size)
{
if (nwio->tio == QSE_NULL)
2012-07-28 14:43:59 +00:00
{
2012-04-27 14:33:14 +00:00
return nwio_write (nwio, data, size);
2012-07-28 14:43:59 +00:00
}
2012-04-27 14:33:14 +00:00
else
{
qse_ssize_t n;
nwio->errnum = QSE_NWIO_ENOERR;
n = qse_tio_write (nwio->tio, data, size);
if (n <= -1 && nwio->errnum == QSE_NWIO_ENOERR)
nwio->errnum = tio_errnum_to_nwio_errnum (nwio->tio);
return n;
}
}
/* ---------------------------------------------------------- */
static qse_ssize_t socket_input (
qse_tio_t* tio, qse_tio_cmd_t cmd, void* buf, qse_size_t size)
{
if (cmd == QSE_TIO_DATA)
{
qse_nwio_t* nwio;
nwio = *(qse_nwio_t**)QSE_XTN(tio);
QSE_ASSERT (nwio != QSE_NULL);
return nwio_read (nwio, buf, size);
}
return 0;
}
static qse_ssize_t socket_output (
qse_tio_t* tio, qse_tio_cmd_t cmd, void* buf, qse_size_t size)
{
if (cmd == QSE_TIO_DATA)
{
qse_nwio_t* nwio;
nwio = *(qse_nwio_t**)QSE_XTN(tio);
QSE_ASSERT (nwio != QSE_NULL);
return nwio_write (nwio, buf, size);
}
return 0;
}