luasocket/src/usocket.c

350 lines
12 KiB
C
Raw Normal View History

/*=========================================================================*\
* Socket compatibilization module for Unix
* LuaSocket toolkit
*
2004-07-02 20:44:05 +02:00
* The code is now interrupt-safe.
* The penalty of calling select to avoid busy-wait is only paid when
* the I/O call fail in the first place.
2004-01-17 09:02:04 +01:00
*
* RCS ID: $Id$
\*=========================================================================*/
2004-07-02 20:44:05 +02:00
#include <string.h>
#include <signal.h>
2002-07-08 22:14:09 +02:00
#include "socket.h"
2002-07-08 22:14:09 +02:00
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
2004-07-01 05:32:09 +02:00
int sock_open(void) {
/* instals a handler to ignore sigpipe or it will crash us */
signal(SIGPIPE, SIG_IGN);
2003-03-21 02:07:23 +01:00
return 1;
2002-07-08 22:14:09 +02:00
}
2004-05-30 23:36:22 +02:00
/*-------------------------------------------------------------------------*\
* Close module
\*-------------------------------------------------------------------------*/
2004-07-01 05:32:09 +02:00
int sock_close(void) {
2004-05-30 23:36:22 +02:00
return 1;
}
/*-------------------------------------------------------------------------*\
* Close and inutilize socket
\*-------------------------------------------------------------------------*/
2004-07-01 05:32:09 +02:00
void sock_destroy(p_sock ps) {
if (*ps != SOCK_INVALID) {
sock_setblocking(ps);
close(*ps);
*ps = SOCK_INVALID;
}
2003-05-25 03:54:13 +02:00
}
/*-------------------------------------------------------------------------*\
2004-07-01 05:32:09 +02:00
* Select with timeout control
\*-------------------------------------------------------------------------*/
2004-07-01 05:32:09 +02:00
int sock_select(int n, fd_set *rfds, fd_set *wfds, fd_set *efds, p_tm tm) {
int ret;
do {
struct timeval tv;
double t = tm_getretry(tm);
tv.tv_sec = (int) t;
tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6);
ret = select(n, rfds, wfds, efds, t >= 0.0? &tv: NULL);
} while (ret < 0 && errno == EINTR);
return ret;
}
/*-------------------------------------------------------------------------*\
* Creates and sets up a socket
\*-------------------------------------------------------------------------*/
2004-07-01 05:32:09 +02:00
const char *sock_create(p_sock ps, int domain, int type, int protocol) {
2003-05-25 03:54:13 +02:00
t_sock sock = socket(domain, type, protocol);
2004-07-01 05:32:09 +02:00
if (sock == SOCK_INVALID) return sock_strerror();
2003-05-25 03:54:13 +02:00
*ps = sock;
return NULL;
2003-05-25 03:54:13 +02:00
}
/*-------------------------------------------------------------------------*\
* Connects or returns error message
\*-------------------------------------------------------------------------*/
2004-07-01 05:32:09 +02:00
const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len, p_tm tm) {
2004-01-17 09:02:04 +01:00
t_sock sock = *ps;
int err;
/* don't call on closed socket */
2004-07-01 05:32:09 +02:00
if (sock == SOCK_INVALID) return io_strerror(IO_CLOSED);
/* ask system to connect */
2004-06-21 00:19:54 +02:00
do err = connect(sock, addr, addr_len);
while (err < 0 && errno == EINTR);
/* if no error, we're done */
if (err == 0) return NULL;
/* make sure the system is trying to connect */
2004-07-01 05:32:09 +02:00
if (errno != EINPROGRESS) return sock_strerror();
/* optimize for timeout = 0 */
if (tm_get(tm) == 0.0) return io_strerror(IO_TIMEOUT);
/* wait for a timeout or for the system's answer */
for ( ;; ) {
2004-07-01 05:32:09 +02:00
fd_set rfds, wfds;
FD_ZERO(&rfds); FD_SET(sock, &rfds);
FD_ZERO(&wfds); FD_SET(sock, &wfds);
/* we run select to avoid busy waiting */
2004-07-01 05:32:09 +02:00
err = sock_select(sock+1, &rfds, &wfds, NULL, tm);
/* if there was an event, check what happened */
if (err > 0) {
2004-01-17 09:02:04 +01:00
char dummy;
/* recv will set errno to the value a blocking connect would set */
2004-07-01 05:32:09 +02:00
if (err > 1 && FD_ISSET(sock, &rfds) &&
recv(sock, &dummy, 0, 0) < 0 && errno != EAGAIN)
return sock_strerror();
else
return NULL;
/* if no event happened, there was a timeout */
2004-07-01 05:32:09 +02:00
} else if (err == 0) return io_strerror(IO_TIMEOUT);
}
2004-07-01 05:32:09 +02:00
return sock_strerror();
2003-05-25 03:54:13 +02:00
}
/*-------------------------------------------------------------------------*\
* Binds or returns error message
\*-------------------------------------------------------------------------*/
2004-07-01 05:32:09 +02:00
const char *sock_bind(p_sock ps, SA *addr, socklen_t addr_len) {
const char *err = NULL;
sock_setblocking(ps);
2004-07-01 05:32:09 +02:00
if (bind(*ps, addr, addr_len) < 0) err = sock_strerror();
sock_setnonblocking(ps);
return err;
2003-05-25 03:54:13 +02:00
}
/*-------------------------------------------------------------------------*\
*
\*-------------------------------------------------------------------------*/
2004-07-01 05:32:09 +02:00
const char* sock_listen(p_sock ps, int backlog) {
const char *err = NULL;
sock_setblocking(ps);
2004-07-01 05:32:09 +02:00
if (listen(*ps, backlog)) err = sock_strerror();
sock_setnonblocking(ps);
return err;
2003-05-25 03:54:13 +02:00
}
/*-------------------------------------------------------------------------*\
*
\*-------------------------------------------------------------------------*/
2004-07-01 05:32:09 +02:00
void sock_shutdown(p_sock ps, int how) {
sock_setblocking(ps);
shutdown(*ps, how);
sock_setnonblocking(ps);
}
/*-------------------------------------------------------------------------*\
* Accept with timeout
\*-------------------------------------------------------------------------*/
const char *sock_accept(p_sock ps, p_sock pa, SA *addr,
2004-07-01 05:32:09 +02:00
socklen_t *addr_len, p_tm tm) {
2003-05-25 03:54:13 +02:00
t_sock sock = *ps;
SA dummy_addr;
socklen_t dummy_len = sizeof(dummy_addr);
2004-07-01 05:32:09 +02:00
if (sock == SOCK_INVALID) return io_strerror(IO_CLOSED);
if (!addr) addr = &dummy_addr;
if (!addr_len) addr_len = &dummy_len;
for (;;) {
int err;
fd_set fds;
/* try to accept */
2004-06-21 00:19:54 +02:00
do *pa = accept(sock, addr, addr_len);
while (*pa < 0 && errno == EINTR);
/* if result is valid, we are done */
2004-06-21 08:07:58 +02:00
if (*pa != SOCK_INVALID) return NULL;
/* find out if we failed for a fatal reason */
2004-07-01 05:32:09 +02:00
/* if connection was aborted, we can try again if we have time */
if (errno != EAGAIN && errno != ECONNABORTED) return sock_strerror();
/* optimize for timeout = 0 case */
if (tm_get(tm) == 0.0) return io_strerror(IO_TIMEOUT);
/* call select to avoid busy-wait. */
FD_ZERO(&fds);
FD_SET(sock, &fds);
2004-07-01 05:32:09 +02:00
err = sock_select(sock+1, &fds, NULL, NULL, tm);
if (err == 0) return io_strerror(IO_TIMEOUT);
2004-07-02 20:44:05 +02:00
else if (err < 0) break;
}
2004-07-02 20:44:05 +02:00
return sock_strerror();
2002-07-08 22:14:09 +02:00
}
/*-------------------------------------------------------------------------*\
* Send with timeout
\*-------------------------------------------------------------------------*/
2004-07-01 05:32:09 +02:00
int sock_send(p_sock ps, const char *data, size_t count, size_t *sent, p_tm tm)
2002-07-08 22:14:09 +02:00
{
2003-05-25 03:54:13 +02:00
t_sock sock = *ps;
/* avoid making system calls on closed sockets */
2003-11-27 01:30:54 +01:00
if (sock == SOCK_INVALID) return IO_CLOSED;
2004-07-01 05:32:09 +02:00
/* loop until we send something or we give up on error */
for ( ;; ) {
2004-06-23 03:08:54 +02:00
int ret;
fd_set fds;
2004-07-01 05:32:09 +02:00
ssize_t put;
/* make sure we repeat in case the call was interrupted */
do put = send(sock, data, count, 0);
while (put < 0 && errno == EINTR);
/* if we sent something, get out */
if (put > 0) {
*sent = put;
return IO_DONE;
}
/* deal with failure */
2002-07-08 22:14:09 +02:00
*sent = 0;
/* here we know the connection has been closed */
2004-07-01 05:32:09 +02:00
if (put < 0 && errno == EPIPE) return IO_CLOSED;
/* send shouldn't return zero and we can only proceed if
* there was no serious error */
if (put == 0 || errno != EAGAIN) return IO_USER;
/* optimize for the timeout = 0 case */
if (tm_get(tm) == 0.0) return IO_TIMEOUT;
/* run select to avoid busy wait */
FD_ZERO(&fds);
FD_SET(sock, &fds);
2004-07-01 05:32:09 +02:00
ret = sock_select(sock+1, NULL, &fds, NULL, tm);
2004-06-23 03:08:54 +02:00
if (ret == 0) return IO_TIMEOUT;
2004-07-02 20:44:05 +02:00
else if (ret < 0) break;
2004-07-01 05:32:09 +02:00
/* otherwise, try sending again */
}
2004-07-02 20:44:05 +02:00
return IO_USER;
2002-07-08 22:14:09 +02:00
}
/*-------------------------------------------------------------------------*\
* Sendto with timeout
\*-------------------------------------------------------------------------*/
2003-05-25 03:54:13 +02:00
int sock_sendto(p_sock ps, const char *data, size_t count, size_t *sent,
2004-07-01 05:32:09 +02:00
SA *addr, socklen_t addr_len, p_tm tm)
2002-07-08 22:14:09 +02:00
{
2003-05-25 03:54:13 +02:00
t_sock sock = *ps;
2004-07-01 05:32:09 +02:00
/* avoid making system calls on closed sockets */
2003-11-27 01:30:54 +01:00
if (sock == SOCK_INVALID) return IO_CLOSED;
2004-07-01 05:32:09 +02:00
/* loop until we send something or we give up on error */
for ( ;; ) {
2004-06-23 03:08:54 +02:00
int ret;
fd_set fds;
2004-07-01 05:32:09 +02:00
ssize_t put;
do put = sendto(sock, data, count, 0, addr, addr_len);
while (put < 0 && errno == EINTR);
if (put > 0) {
*sent = put;
return IO_DONE;
}
2002-07-08 22:14:09 +02:00
*sent = 0;
2004-07-01 05:32:09 +02:00
if (put < 0 && errno == EPIPE) return IO_CLOSED;
if (put == 0 || errno != EAGAIN) return IO_USER;
if (tm_get(tm) == 0.0) return IO_TIMEOUT;
FD_ZERO(&fds);
FD_SET(sock, &fds);
2004-07-01 05:32:09 +02:00
ret = sock_select(sock+1, NULL, &fds, NULL, tm);
2004-06-23 03:08:54 +02:00
if (ret == 0) return IO_TIMEOUT;
2004-07-02 20:44:05 +02:00
else if (ret < 0) break;
2004-07-01 05:32:09 +02:00
}
2004-07-02 20:44:05 +02:00
return IO_USER;
2002-07-08 22:14:09 +02:00
}
/*-------------------------------------------------------------------------*\
* Receive with timeout
\*-------------------------------------------------------------------------*/
2004-07-01 05:32:09 +02:00
int sock_recv(p_sock ps, char *data, size_t count, size_t *got, p_tm tm) {
2003-05-25 03:54:13 +02:00
t_sock sock = *ps;
2003-11-27 01:30:54 +01:00
if (sock == SOCK_INVALID) return IO_CLOSED;
2004-07-01 05:32:09 +02:00
for ( ;; ) {
fd_set fds;
int ret;
2004-07-01 05:32:09 +02:00
ssize_t taken;
do taken = read(sock, data, count);
while (taken < 0 && errno == EINTR);
if (taken > 0) {
*got = taken;
return IO_DONE;
}
2002-07-08 22:14:09 +02:00
*got = 0;
if (taken == 0) return IO_CLOSED;
2004-07-01 05:32:09 +02:00
if (errno != EAGAIN) return IO_USER;
if (tm_get(tm) == 0.0) return IO_TIMEOUT;
FD_ZERO(&fds);
FD_SET(sock, &fds);
2004-07-01 05:32:09 +02:00
ret = sock_select(sock+1, &fds, NULL, NULL, tm);
if (ret == 0) return IO_TIMEOUT;
2004-07-02 20:44:05 +02:00
else if (ret < 0) break;
2004-07-01 05:32:09 +02:00
}
2004-07-02 20:44:05 +02:00
return IO_USER;
2002-07-08 22:14:09 +02:00
}
/*-------------------------------------------------------------------------*\
* Recvfrom with timeout
\*-------------------------------------------------------------------------*/
2003-05-25 03:54:13 +02:00
int sock_recvfrom(p_sock ps, char *data, size_t count, size_t *got,
2004-07-01 05:32:09 +02:00
SA *addr, socklen_t *addr_len, p_tm tm) {
2003-05-25 03:54:13 +02:00
t_sock sock = *ps;
2003-11-27 01:30:54 +01:00
if (sock == SOCK_INVALID) return IO_CLOSED;
2004-07-01 05:32:09 +02:00
for ( ;; ) {
fd_set fds;
int ret;
2004-07-01 05:32:09 +02:00
ssize_t taken;
do taken = recvfrom(sock, data, count, 0, addr, addr_len);
while (taken < 0 && errno == EINTR);
if (taken > 0) {
*got = taken;
return IO_DONE;
}
2002-07-08 22:14:09 +02:00
*got = 0;
if (taken == 0) return IO_CLOSED;
2004-07-01 05:32:09 +02:00
if (errno != EAGAIN) return IO_USER;
if (tm_get(tm) == 0.0) return IO_TIMEOUT;
FD_ZERO(&fds);
FD_SET(sock, &fds);
2004-07-01 05:32:09 +02:00
ret = sock_select(sock+1, &fds, NULL, NULL, tm);
if (ret == 0) return IO_TIMEOUT;
2004-07-02 20:44:05 +02:00
else if (ret < 0) break;
2004-07-01 05:32:09 +02:00
}
2004-07-02 20:44:05 +02:00
return IO_USER;
2002-07-08 22:14:09 +02:00
}
/*-------------------------------------------------------------------------*\
* Put socket into blocking mode
\*-------------------------------------------------------------------------*/
2004-07-01 05:32:09 +02:00
void sock_setblocking(p_sock ps) {
int flags = fcntl(*ps, F_GETFL, 0);
flags &= (~(O_NONBLOCK));
fcntl(*ps, F_SETFL, flags);
}
/*-------------------------------------------------------------------------*\
* Put socket into non-blocking mode
\*-------------------------------------------------------------------------*/
2004-07-01 05:32:09 +02:00
void sock_setnonblocking(p_sock ps) {
int flags = fcntl(*ps, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(*ps, F_SETFL, flags);
}
/*-------------------------------------------------------------------------*\
* Error translation functions
\*-------------------------------------------------------------------------*/
2004-07-01 05:32:09 +02:00
const char *sock_hoststrerror(void) {
2004-07-02 20:44:05 +02:00
switch (h_errno) {
case HOST_NOT_FOUND:
return "host not found";
default:
return hstrerror(h_errno);
}
2002-07-08 22:14:09 +02:00
}
2004-07-01 05:32:09 +02:00
/* make sure important error messages are standard */
const char *sock_strerror(void) {
switch (errno) {
case EADDRINUSE:
return "address already in use";
default:
return strerror(errno);
2002-07-08 22:14:09 +02:00
}
}
2004-07-01 05:32:09 +02:00
const char *sock_geterr(p_sock ps, int code) {
2004-07-01 05:47:22 +02:00
(void) ps;
(void) code;
2004-07-01 05:32:09 +02:00
return sock_strerror();
2002-07-08 22:14:09 +02:00
}