Fixed a bunch of stuff. Added mike's patches.

This commit is contained in:
Diego Nehab 2004-07-16 06:48:48 +00:00
parent 9a79d500eb
commit e4e2223cff
13 changed files with 249 additions and 327 deletions

2
FIX
View File

@ -1,9 +1,9 @@
fix bug that caused select return tables not to be associative on windows
compiles with g++ compiles with g++
new sample unix domain support new sample unix domain support
new sample LPD support new sample LPD support
comprehensive error messages in the default case. comprehensive error messages in the default case.
new getstats method to help throttle. new getstats method to help throttle.
setup error messages in the default case.
listen defaults to 32 backlog listen defaults to 32 backlog
smtp/ftp/http fail gracefully smtp/ftp/http fail gracefully
accept/connect/select interrupt safe accept/connect/select interrupt safe

6
TODO
View File

@ -1,6 +1,6 @@
test associativity of socket.select
probably if is dirty, no assoc is created. wonder why...
change sock:send to use indices just like string.sub?
use mike's "don't set to blocking before closing unless needed" patch?
take a look at DB's smtp patch take a look at DB's smtp patch
optmize aux_getgroupudata (Mike idea) optmize aux_getgroupudata (Mike idea)
@ -12,7 +12,6 @@ add error message stuff to the manual
make sure all modules that can use it actually use socket.newtry make sure all modules that can use it actually use socket.newtry
adicionar exemplos de expansão: pipe, local, named pipe adicionar exemplos de expansão: pipe, local, named pipe
Add service name translation.
testar os options! testar os options!
- Thread-safe - Thread-safe
- proteger get*by*.* com um mutex GLOBAL! - proteger get*by*.* com um mutex GLOBAL!
@ -29,3 +28,4 @@ testar os options!
*use GetSystemTimeAsFileTime in windows (WinCE will suffer document) *use GetSystemTimeAsFileTime in windows (WinCE will suffer document)
*add getstats to the manual *add getstats to the manual
*Fazer compilar com g++ *Fazer compilar com g++
*test associativity of socket.select

View File

@ -67,13 +67,13 @@
CharacterSet="2"> CharacterSet="2">
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
AdditionalIncludeDirectories="H:\include" AdditionalIncludeDirectories="../../include"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LUASOCKET_EXPORTS;LUASOCKET_API=__declspec(dllexport); LUASOCKET_DEBUG" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LUASOCKET_EXPORTS;LUASOCKET_API=__declspec(dllexport); LUASOCKET_DEBUG"
RuntimeLibrary="0" RuntimeLibrary="0"
UsePrecompiledHeader="0" UsePrecompiledHeader="0"
WarningLevel="3" WarningLevel="3"
Detect64BitPortabilityProblems="TRUE" Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="3"/> DebugInformationFormat="0"/>
<Tool <Tool
Name="VCCustomBuildTool"/> Name="VCCustomBuildTool"/>
<Tool <Tool

View File

@ -67,13 +67,13 @@
CharacterSet="2"> CharacterSet="2">
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
AdditionalIncludeDirectories="H:\include" AdditionalIncludeDirectories="../../include"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MIME_EXPORTS; MIME_API=__declspec(dllexport)" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MIME_EXPORTS; MIME_API=__declspec(dllexport)"
RuntimeLibrary="0" RuntimeLibrary="0"
UsePrecompiledHeader="0" UsePrecompiledHeader="0"
WarningLevel="3" WarningLevel="3"
Detect64BitPortabilityProblems="TRUE" Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="3"/> DebugInformationFormat="0"/>
<Tool <Tool
Name="VCCustomBuildTool"/> Name="VCCustomBuildTool"/>
<Tool <Tool

View File

@ -1,25 +1,25 @@
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- TCP sample: Little program to dump lines received at a given port -- TCP sample: Little program to dump lines received at a given port
-- LuaSocket sample files -- LuaSocket sample files
-- Author: Diego Nehab -- Author: Diego Nehab
-- RCS ID: $Id$ -- RCS ID: $Id$
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
local socket = require("socket") local socket = require("socket")
host = host or "*" host = host or "*"
port = port or 8080 port = port or 8080
if arg then if arg then
host = arg[1] or host host = arg[1] or host
port = arg[2] or port port = arg[2] or port
end end
print("Binding to host '" ..host.. "' and port " ..port.. "...") print("Binding to host '" ..host.. "' and port " ..port.. "...")
s = socket.try(socket.bind(host, port)) s = socket.try(socket.bind(host, port))
i, p = socket.try(s:getsockname()) i, p = socket.try(s:getsockname())
print("Waiting connection from talker on " .. i .. ":" .. p .. "...") print("Waiting connection from talker on " .. i .. ":" .. p .. "...")
c = socket.try(s:accept()) c = socket.try(s:accept())
print("Connected. Here is the stuff:") print("Connected. Here is the stuff:")
l, e = c:receive() l, e = c:receive()
while not e do while not e do
print(l) print(l)
l, e = c:receive() l, e = c:receive()
end end
print(e) print(e)

View File

@ -209,8 +209,8 @@ const char *inet_tryconnect(p_sock ps, const char *address,
memset(&remote, 0, sizeof(remote)); memset(&remote, 0, sizeof(remote));
remote.sin_family = AF_INET; remote.sin_family = AF_INET;
remote.sin_port = htons(port); remote.sin_port = htons(port);
if (strcmp(address, "*")) { if (strcmp(address, "*")) {
if (!strlen(address) || !inet_aton(address, &remote.sin_addr)) { if (!inet_aton(address, &remote.sin_addr)) {
struct hostent *hp = NULL; struct hostent *hp = NULL;
struct in_addr **addr; struct in_addr **addr;
err = sock_gethostbyname(address, &hp); err = sock_gethostbyname(address, &hp);
@ -236,8 +236,7 @@ const char *inet_trybind(p_sock ps, const char *address, unsigned short port)
local.sin_addr.s_addr = htonl(INADDR_ANY); local.sin_addr.s_addr = htonl(INADDR_ANY);
local.sin_port = htons(port); local.sin_port = htons(port);
local.sin_family = AF_INET; local.sin_family = AF_INET;
if (strcmp(address, "*") && if (strcmp(address, "*") && !inet_aton(address, &local.sin_addr)) {
(!strlen(address) || !inet_aton(address, &local.sin_addr))) {
struct hostent *hp = NULL; struct hostent *hp = NULL;
struct in_addr **addr; struct in_addr **addr;
err = sock_gethostbyname(address, &hp); err = sock_gethostbyname(address, &hp);

View File

@ -24,18 +24,19 @@ enum {
IO_DONE = 0, /* operation completed successfully */ IO_DONE = 0, /* operation completed successfully */
IO_TIMEOUT = -1, /* operation timed out */ IO_TIMEOUT = -1, /* operation timed out */
IO_CLOSED = -2, /* the connection has been closed */ IO_CLOSED = -2, /* the connection has been closed */
IO_CLIPPED = -3 /* maxium bytes count reached */ IO_CLIPPED = -3, /* maxium bytes count reached */
IO_UNKNOWN = -4
}; };
/* interface to error message function */ /* interface to error message function */
typedef const char *(*p_error) ( typedef const char *(*p_error) (
void *ctx, /* context needed by send */ void *ctx, /* context needed by send */
int err /* error code */ int err /* error code */
); );
/* interface to send function */ /* interface to send function */
typedef int (*p_send) ( typedef int (*p_send) (
void *ctx, /* context needed by send */ void *ctx, /* context needed by send */
const char *data, /* pointer to buffer with data to send */ const char *data, /* pointer to buffer with data to send */
size_t count, /* number of bytes to send from buffer */ size_t count, /* number of bytes to send from buffer */
size_t *sent, /* number of bytes sent uppon return */ size_t *sent, /* number of bytes sent uppon return */
@ -44,7 +45,7 @@ typedef int (*p_send) (
/* interface to recv function */ /* interface to recv function */
typedef int (*p_recv) ( typedef int (*p_recv) (
void *ctx, /* context needed by recv */ void *ctx, /* context needed by recv */
char *data, /* pointer to buffer where data will be writen */ char *data, /* pointer to buffer where data will be writen */
size_t count, /* number of bytes to receive into buffer */ size_t count, /* number of bytes to receive into buffer */
size_t *got, /* number of bytes received uppon return */ size_t *got, /* number of bytes received uppon return */
@ -61,6 +62,6 @@ typedef struct t_io_ {
typedef t_io *p_io; typedef t_io *p_io;
void io_init(p_io io, p_send send, p_recv recv, p_error error, void *ctx); void io_init(p_io io, p_send send, p_recv recv, p_error error, void *ctx);
const char *io_strerror(int err); const char *io_strerror(int err);
#endif /* IO_H */ #endif /* IO_H */

View File

@ -64,7 +64,7 @@ static int global_select(lua_State *L) {
tm_init(&tm, t, -1); tm_init(&tm, t, -1);
max_fd = collect_fd(L, 2, max_fd, itab, &wset); max_fd = collect_fd(L, 2, max_fd, itab, &wset);
ret = sock_select(max_fd+1, &rset, &wset, NULL, &tm); ret = sock_select(max_fd+1, &rset, &wset, NULL, &tm);
if (ret > 0 || (ret == 0 && ndirty > 0)) { if (ret > 0 || ndirty > 0) {
return_fd(L, &rset, max_fd+1, itab, rtab, ndirty); return_fd(L, &rset, max_fd+1, itab, rtab, ndirty);
return_fd(L, &wset, max_fd+1, itab, wtab, 0); return_fd(L, &wset, max_fd+1, itab, wtab, 0);
make_assoc(L, rtab); make_assoc(L, rtab);

View File

@ -192,7 +192,8 @@ int tm_lua_sleep(lua_State *L)
struct timespec t, r; struct timespec t, r;
t.tv_sec = (int) n; t.tv_sec = (int) n;
n -= t.tv_sec; n -= t.tv_sec;
t.tv_nsec = (int) (n * 1000000000) % 1000000000; t.tv_nsec = (int) (n * 1000000000);
if (t.tv_nsec >= 1000000000) t.tv_nsec = 999999999;
nanosleep(&t, &r); nanosleep(&t, &r);
#endif #endif
return 0; return 0;

View File

@ -19,7 +19,7 @@
#include "timeout.h" #include "timeout.h"
#include "socket.h" #include "socket.h"
#define UDP_DATAGRAMSIZE 576 #define UDP_DATAGRAMSIZE 8192
typedef struct t_udp_ { typedef struct t_udp_ {
t_sock sock; t_sock sock;

View File

@ -179,11 +179,11 @@ int sock_connect(p_sock ps, SA *addr, socklen_t len, p_tm tm) {
int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *len, p_tm tm) { int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *len, p_tm tm) {
SA daddr; SA daddr;
socklen_t dlen = sizeof(daddr); socklen_t dlen = sizeof(daddr);
int err;
if (*ps == SOCK_INVALID) return IO_CLOSED; if (*ps == SOCK_INVALID) return IO_CLOSED;
if (!addr) addr = &daddr; if (!addr) addr = &daddr;
if (!len) len = &dlen; if (!len) len = &dlen;
for ( ;; ) { for ( ;; ) {
int err;
if ((*pa = accept(*ps, addr, len)) != SOCK_INVALID) return IO_DONE; if ((*pa = accept(*ps, addr, len)) != SOCK_INVALID) return IO_DONE;
err = errno; err = errno;
if (err == EINTR) continue; if (err == EINTR) continue;
@ -191,7 +191,7 @@ int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *len, p_tm tm) {
if ((err = sock_waitfd(*ps, WAITFD_R, tm)) != IO_DONE) return err; if ((err = sock_waitfd(*ps, WAITFD_R, tm)) != IO_DONE) return err;
} }
/* can't reach here */ /* can't reach here */
return err; return IO_UNKNOWN;
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
@ -223,7 +223,7 @@ int sock_send(p_sock ps, const char *data, size_t count, size_t *sent, p_tm tm)
if ((err = sock_waitfd(*ps, WAITFD_W, tm)) != IO_DONE) return err; if ((err = sock_waitfd(*ps, WAITFD_W, tm)) != IO_DONE) return err;
} }
/* can't reach here */ /* can't reach here */
return err; return IO_UNKNOWN;
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
@ -247,7 +247,7 @@ int sock_sendto(p_sock ps, const char *data, size_t count, size_t *sent,
if (err != EAGAIN) return err; if (err != EAGAIN) return err;
if ((err = sock_waitfd(*ps, WAITFD_W, tm)) != IO_DONE) return err; if ((err = sock_waitfd(*ps, WAITFD_W, tm)) != IO_DONE) return err;
} }
return err; return IO_UNKNOWN;
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
@ -269,7 +269,7 @@ int sock_recv(p_sock ps, char *data, size_t count, size_t *got, p_tm tm) {
if (err != EAGAIN) return err; if (err != EAGAIN) return err;
if ((err = sock_waitfd(*ps, WAITFD_R, tm)) != IO_DONE) return err; if ((err = sock_waitfd(*ps, WAITFD_R, tm)) != IO_DONE) return err;
} }
return err; return IO_UNKNOWN;
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
@ -292,7 +292,7 @@ int sock_recvfrom(p_sock ps, char *data, size_t count, size_t *got,
if (err != EAGAIN) return err; if (err != EAGAIN) return err;
if ((err = sock_waitfd(*ps, WAITFD_R, tm)) != IO_DONE) return err; if ((err = sock_waitfd(*ps, WAITFD_R, tm)) != IO_DONE) return err;
} }
return err; return IO_UNKNOWN;
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
@ -345,10 +345,10 @@ const char *sock_strerror(int err) {
switch (err) { switch (err) {
case EADDRINUSE: return "eaddrinuse"; case EADDRINUSE: return "eaddrinuse";
case EACCES: return "eaccess"; case EACCES: return "eaccess";
case ECONNABORTED: return "econnaborted";
case ECONNREFUSED: return "econnrefused"; case ECONNREFUSED: return "econnrefused";
case ECONNRESET: return "econnreset"; case ECONNABORTED: return "closed";
case ETIMEDOUT: return "etimedout"; case ECONNRESET: return "closed";
case ETIMEDOUT: return "timedout";
default: return strerror(errno); default: return strerror(errno);
} }
} }

View File

@ -13,7 +13,6 @@
/* WinSock doesn't have a strerror... */ /* WinSock doesn't have a strerror... */
static const char *wstrerror(int err); static const char *wstrerror(int err);
static int wisclosed(int err);
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Initializes module * Initializes module
@ -38,15 +37,44 @@ int sock_close(void) {
return 1; return 1;
} }
/*-------------------------------------------------------------------------*\
* Wait for readable/writable/connected socket with timeout
\*-------------------------------------------------------------------------*/
#define WAITFD_R 1
#define WAITFD_W 2
#define WAITFD_E 4
#define WAITFD_C (WAITFD_E|WAITFD_W)
static int sock_waitfd(t_sock fd, int sw, p_tm tm) {
int ret;
fd_set rfds, wfds, efds, *rp = NULL, *wp = NULL, *ep = NULL;
struct timeval tv, *tp = NULL;
double t;
if (tm_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */
if (sw & WAITFD_R) { FD_ZERO(&rfds); FD_SET(fd, &rfds); rp = &rfds; }
if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(fd, &wfds); wp = &wfds; }
if (sw & WAITFD_C) { FD_ZERO(&efds); FD_SET(fd, &efds); ep = &efds; }
if ((t = tm_get(tm)) >= 0.0) {
tv.tv_sec = (int) t;
tv.tv_usec = (int) ((t-tv.tv_sec)*1.0e6);
tp = &tv;
}
ret = select(0, rp, wp, ep, tp);
if (ret == -1) return WSAGetLastError();
if (ret == 0) return IO_TIMEOUT;
if (sw == WAITFD_C && FD_ISSET(fd, &efds)) return IO_CLOSED;
return IO_DONE;
}
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Select with int timeout in ms * Select with int timeout in ms
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
int sock_select(int n, fd_set *rfds, fd_set *wfds, fd_set *efds, p_tm tm) { int sock_select(int n, fd_set *rfds, fd_set *wfds, fd_set *efds, p_tm tm) {
struct timeval tv; struct timeval tv;
double t = tm_get(tm); double t = tm_get(tm);
tv.tv_sec = (int) t; tv.tv_sec = (int) t;
tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6); tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6);
return select(n, rfds, wfds, efds, t >= 0.0? &tv: NULL); return select(0, rfds, wfds, efds, t >= 0.0? &tv: NULL);
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
@ -72,62 +100,45 @@ void sock_shutdown(p_sock ps, int how) {
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Creates and sets up a socket * Creates and sets up a socket
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
const char *sock_create(p_sock ps, int domain, int type, int protocol) { int sock_create(p_sock ps, int domain, int type, int protocol) {
t_sock sock = socket(domain, type, protocol); *ps = socket(domain, type, protocol);
if (sock == SOCK_INVALID) return sock_strerror(); if (*ps != SOCK_INVALID) return IO_DONE;
*ps = sock; else return WSAGetLastError();
return NULL;
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Connects or returns error message * Connects or returns error message
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len, p_tm tm) { int sock_connect(p_sock ps, SA *addr, socklen_t len, p_tm tm) {
t_sock sock = *ps;
int err; int err;
fd_set efds, wfds;
/* don't call on closed socket */ /* don't call on closed socket */
if (sock == SOCK_INVALID) return io_strerror(IO_CLOSED); if (*ps == SOCK_INVALID) return IO_CLOSED;
/* ask system to connect */ /* ask system to connect */
err = connect(sock, addr, addr_len); if (connect(*ps, addr, len) == 0) return IO_DONE;
/* if no error, we're done */
if (err == 0) return NULL;
/* make sure the system is trying to connect */ /* make sure the system is trying to connect */
err = WSAGetLastError(); err = WSAGetLastError();
if (err != WSAEWOULDBLOCK) return wstrerror(err); if (err != WSAEWOULDBLOCK && err != WSAEINPROGRESS) return err;
/* optimize for timeout=0 */ /* we wait until something happens */
if (tm_get(tm) == 0.0) return io_strerror(IO_TIMEOUT); if ((err = sock_waitfd(*ps, WAITFD_C, tm)) == IO_CLOSED) {
/* wait for a timeout or for the system's answer */ int len = sizeof(err);
FD_ZERO(&wfds); FD_SET(sock, &wfds); /* give windows time to set the error (yes, disgusting) */
FD_ZERO(&efds); FD_SET(sock, &efds); Sleep(0);
/* we run select to wait */ /* find out why we failed */
err = sock_select(0, NULL, &wfds, &efds, tm); getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *)&err, &len);
/* if select returned due to an event */ /* we KNOW there was an error. if why is 0, we will return
if (err > 0) { * "unknown error", but it's not really our fault */
/* if was in efds, we failed */ return err > 0? err: IO_UNKNOWN;
if (FD_ISSET(sock, &efds)) { /* here we deal with the case in which it worked, timedout or weird errors */
int why, len = sizeof(why); } else return err;
/* give windows time to set the error (disgusting) */
Sleep(0);
/* find out why we failed */
getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&why, &len);
/* we KNOW there was an error. if why is 0, we will return
* "unknown error", but it's not really our fault */
return wstrerror(why);
/* otherwise it must be in wfds, so we succeeded */
} else return NULL;
/* if no event happened, we timed out */
} else if (err == 0) return io_strerror(IO_TIMEOUT);
return sock_strerror();
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Binds or returns error message * Binds or returns error message
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
const char *sock_bind(p_sock ps, SA *addr, socklen_t addr_len) { int sock_bind(p_sock ps, SA *addr, socklen_t len) {
const char *err = NULL; int err = IO_DONE;
sock_setblocking(ps); sock_setblocking(ps);
if (bind(*ps, addr, addr_len) < 0) err = sock_strerror(); if (bind(*ps, addr, len) < 0) err = WSAGetLastError();
sock_setnonblocking(ps); sock_setnonblocking(ps);
return err; return err;
} }
@ -135,10 +146,10 @@ const char *sock_bind(p_sock ps, SA *addr, socklen_t addr_len) {
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* *
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
const char *sock_listen(p_sock ps, int backlog) { int sock_listen(p_sock ps, int backlog) {
const char *err = NULL; int err = IO_DONE;
sock_setblocking(ps); sock_setblocking(ps);
if (listen(*ps, backlog) < 0) err = sock_strerror(); if (listen(*ps, backlog) < 0) err = WSAGetLastError();
sock_setnonblocking(ps); sock_setnonblocking(ps);
return err; return err;
} }
@ -146,35 +157,25 @@ const char *sock_listen(p_sock ps, int backlog) {
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Accept with timeout * Accept with timeout
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
const char *sock_accept(p_sock ps, p_sock pa, SA *addr, int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *len, p_tm tm) {
socklen_t *addr_len, p_tm tm) { SA daddr;
t_sock sock = *ps; socklen_t dlen = sizeof(daddr);
SA dummy_addr; if (*ps == SOCK_INVALID) return IO_CLOSED;
socklen_t dummy_len = sizeof(dummy_addr); if (!addr) addr = &daddr;
if (sock == SOCK_INVALID) return io_strerror(IO_CLOSED); if (!len) len = &dlen;
if (!addr) addr = &dummy_addr; for ( ;; ) {
if (!addr_len) addr_len = &dummy_len;
for (;;) {
fd_set rfds;
int err; int err;
/* try to get client socket */ /* try to get client socket */
*pa = accept(sock, addr, addr_len); if ((*pa = accept(*ps, addr, len)) != SOCK_INVALID) return IO_DONE;
/* if return is valid, we are done */ /* find out why we failed */
if (*pa != SOCK_INVALID) return NULL;
/* otherwise find out why we failed */
err = WSAGetLastError(); err = WSAGetLastError();
/* if we failed because there was no connectoin, keep trying */ /* if we failed because there was no connectoin, keep trying */
if (err != WSAEWOULDBLOCK) return wstrerror(err); if (err != WSAEWOULDBLOCK && err != WSAECONNABORTED) return err;
/* optimize for the timeout=0 case */
if (tm_get(tm) == 0.0) return io_strerror(IO_TIMEOUT);
/* call select to avoid busy wait */ /* call select to avoid busy wait */
FD_ZERO(&rfds); if ((err = sock_waitfd(*ps, WAITFD_R, tm)) != IO_DONE) return err;
FD_SET(sock, &rfds);
err = sock_select(0, &rfds, NULL, NULL, tm);
if (err == 0) return io_strerror(IO_TIMEOUT);
else if (err < 0) break;
} }
return sock_strerror(); /* can't reach here */
return IO_UNKNOWN;
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
@ -182,131 +183,93 @@ const char *sock_accept(p_sock ps, p_sock pa, SA *addr,
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
int sock_send(p_sock ps, const char *data, size_t count, size_t *sent, p_tm tm) int sock_send(p_sock ps, const char *data, size_t count, size_t *sent, p_tm tm)
{ {
t_sock sock = *ps; int err;
/* avoid making system calls on closed sockets */ /* avoid making system calls on closed sockets */
if (sock == SOCK_INVALID) return IO_CLOSED; if (*ps == SOCK_INVALID) return IO_CLOSED;
/* loop until we send something or we give up on error */
*sent = 0;
for ( ;; ) { for ( ;; ) {
fd_set fds;
int ret, put;
/* try to send something */ /* try to send something */
put = send(sock, data, (int) count, 0); int put = send(*ps, data, (int) count, 0);
/* if we sent something, we are done */ /* if we sent something, we are done */
if (put > 0) { if (put > 0) {
*sent = put; *sent = put;
return IO_DONE; return IO_DONE;
} }
/* deal with failure */ /* deal with failure */
*sent = 0; err = WSAGetLastError();
ret = WSAGetLastError();
/* check for connection closed */
if (wisclosed(ret)) return IO_CLOSED;
/* we can only proceed if there was no serious error */ /* we can only proceed if there was no serious error */
if (ret != WSAEWOULDBLOCK) return IO_USER; if (err != WSAEWOULDBLOCK) return err;
/* optimize for the timeout = 0 case */ /* avoid busy wait */
if (tm_get(tm) == 0.0) return IO_TIMEOUT; if ((err = sock_waitfd(*ps, WAITFD_W, tm)) != IO_DONE) return err;
/* run select to avoid busy wait */
FD_ZERO(&fds);
FD_SET(sock, &fds);
ret = sock_select(0, NULL, &fds, NULL, tm);
if (ret == 0) return IO_TIMEOUT;
else if (ret < 0) break;
} }
return IO_USER; /* can't reach here */
return IO_UNKNOWN;
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Sendto with timeout * Sendto with timeout
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
int sock_sendto(p_sock ps, const char *data, size_t count, size_t *sent, int sock_sendto(p_sock ps, const char *data, size_t count, size_t *sent,
SA *addr, socklen_t addr_len, p_tm tm) SA *addr, socklen_t len, p_tm tm)
{ {
t_sock sock = *ps; int err;
/* avoid making system calls on closed sockets */ if (*ps == SOCK_INVALID) return IO_CLOSED;
if (sock == SOCK_INVALID) return IO_CLOSED; *sent = 0;
for ( ;; ) { for ( ;; ) {
fd_set fds; int put = send(*ps, data, (int) count, 0);
int ret, put;
/* try to send something */
put = sendto(sock, data, (int) count, 0, addr, addr_len);
/* if we sent something, we are done */
if (put > 0) { if (put > 0) {
*sent = put; *sent = put;
return IO_DONE; return IO_DONE;
} }
/* deal with failure */ err = WSAGetLastError();
*sent = 0; if (err != WSAEWOULDBLOCK) return err;
ret = WSAGetLastError(); if ((err = sock_waitfd(*ps, WAITFD_W, tm)) != IO_DONE) return err;
/* check for connection closed */
if (wisclosed(ret)) return IO_CLOSED;
/* we can only proceed if there was no serious error */
if (ret != WSAEWOULDBLOCK) 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);
ret = sock_select(0, NULL, &fds, NULL, tm);
if (ret == 0) return IO_TIMEOUT;
else if (ret < 0) break;
} }
return IO_USER; return IO_UNKNOWN;
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Receive with timeout * Receive with timeout
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
int sock_recv(p_sock ps, char *data, size_t count, size_t *got, p_tm tm) int sock_recv(p_sock ps, char *data, size_t count, size_t *got, p_tm tm) {
{ int err;
t_sock sock = *ps; if (*ps == SOCK_INVALID) return IO_CLOSED;
if (sock == SOCK_INVALID) return IO_CLOSED; *got = 0;
for ( ;; ) { for ( ;; ) {
fd_set fds; int taken = recv(*ps, data, (int) count, 0);
int ret, taken;
taken = recv(sock, data, (int) count, 0);
if (taken > 0) { if (taken > 0) {
*got = taken; *got = taken;
return IO_DONE; return IO_DONE;
} }
*got = 0; if (taken == 0) return IO_CLOSED;
if (taken == 0 || wisclosed(ret = WSAGetLastError())) return IO_CLOSED; err = WSAGetLastError();
if (ret != WSAEWOULDBLOCK) return IO_USER; if (err != WSAEWOULDBLOCK) return err;
if (tm_get(tm) == 0.0) return IO_TIMEOUT; if ((err = sock_waitfd(*ps, WAITFD_R, tm)) != IO_DONE) return err;
FD_ZERO(&fds);
FD_SET(sock, &fds);
ret = sock_select(0, &fds, NULL, NULL, tm);
if (ret == 0) return IO_TIMEOUT;
else if (ret < 0) break;
} }
return IO_TIMEOUT; return IO_UNKNOWN;
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Recvfrom with timeout * Recvfrom with timeout
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
int sock_recvfrom(p_sock ps, char *data, size_t count, size_t *got, int sock_recvfrom(p_sock ps, char *data, size_t count, size_t *got,
SA *addr, socklen_t *addr_len, p_tm tm) SA *addr, socklen_t *len, p_tm tm) {
{ int err;
t_sock sock = *ps; if (*ps == SOCK_INVALID) return IO_CLOSED;
if (sock == SOCK_INVALID) return IO_CLOSED; *got = 0;
for ( ;; ) { for ( ;; ) {
fd_set fds; int taken = recvfrom(*ps, data, (int) count, 0, addr, len);
int ret, taken;
taken = recvfrom(sock, data, (int) count, 0, addr, addr_len);
if (taken > 0) { if (taken > 0) {
*got = taken; *got = taken;
return IO_DONE; return IO_DONE;
} }
*got = 0; if (taken == 0) return IO_CLOSED;
if (taken == 0 || wisclosed(ret = WSAGetLastError())) return IO_CLOSED; err = WSAGetLastError();
if (ret != WSAEWOULDBLOCK) return IO_USER; if (err != WSAEWOULDBLOCK) return err;
if (tm_get(tm) == 0.0) return IO_TIMEOUT; if ((err = sock_waitfd(*ps, WAITFD_R, tm)) != IO_DONE) return err;
FD_ZERO(&fds);
FD_SET(sock, &fds);
ret = sock_select(0, &fds, NULL, NULL, tm);
if (ret == 0) return IO_TIMEOUT;
else if (ret < 0) break;
} }
return IO_TIMEOUT; return IO_UNKNOWN;
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
@ -325,139 +288,97 @@ void sock_setnonblocking(p_sock ps) {
ioctlsocket(*ps, FIONBIO, &argp); ioctlsocket(*ps, FIONBIO, &argp);
} }
/*-------------------------------------------------------------------------*\
* DNS helpers
\*-------------------------------------------------------------------------*/
int sock_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) {
*hp = gethostbyaddr(addr, len, AF_INET);
if (*hp) return IO_DONE;
else return h_errno;
}
int sock_gethostbyname(const char *addr, struct hostent **hp) {
*hp = gethostbyname(addr);
if (*hp) return IO_DONE;
else return h_errno;
}
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Error translation functions * Error translation functions
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
const char *sock_hoststrerror(void) { const char *sock_hoststrerror(int err) {
int err = WSAGetLastError(); if (err <= 0) return io_strerror(err);
switch (err) { switch (err) {
case WSAHOST_NOT_FOUND: case WSAHOST_NOT_FOUND: return "host_not_found";
return "host not found"; default: return wstrerror(err);
default:
return wstrerror(err);
} }
} }
const char *sock_strerror(void) { const char *sock_strerror(int err) {
int err = WSAGetLastError(); if (err <= 0) return io_strerror(err);
switch (err) { switch (err) {
case WSAEADDRINUSE: case WSAEADDRINUSE: return "eaddrinuse";
return "address already in use"; case WSAECONNREFUSED: return "econnrefused";
default: case WSAECONNABORTED: return "closed";
return wstrerror(err); case WSAECONNRESET: return "closed";
case WSAETIMEDOUT: return "timeout";
default: return wstrerror(err);
} }
} }
const char *sock_geterr(p_sock ps, int code) { const char *sock_ioerror(p_sock ps, int err) {
(void) ps; (void) ps;
(void) code; return sock_strerror(err);
return sock_strerror();
}
int wisclosed(int err) {
switch (err) {
case WSAECONNRESET:
case WSAECONNABORTED:
case WSAESHUTDOWN:
case WSAENOTCONN:
return 1;
default:
return 0;
}
} }
static const char *wstrerror(int err) { static const char *wstrerror(int err) {
switch (err) { switch (err) {
case WSAEINTR: case WSAEINTR: return "Interrupted function call";
return "WSAEINTR: Interrupted function call"; case WSAEACCES: return "Permission denied";
case WSAEACCES: case WSAEFAULT: return "Bad address";
return "WSAEACCES: Permission denied"; case WSAEINVAL: return "Invalid argument";
case WSAEFAULT: case WSAEMFILE: return "Too many open files";
return "WSAEFAULT: Bad address"; case WSAEWOULDBLOCK: return "Resource temporarily unavailable";
case WSAEINVAL: case WSAEINPROGRESS: return "Operation now in progress";
return "WSAEINVAL: Invalid argument"; case WSAEALREADY: return "Operation already in progress";
case WSAEMFILE: case WSAENOTSOCK: return "Socket operation on nonsocket";
return "WSAEMFILE: Too many open files"; case WSAEDESTADDRREQ: return "Destination address required";
case WSAEWOULDBLOCK: case WSAEMSGSIZE: return "Message too long";
return "WSAEWOULDBLOCK: Resource temporarily unavailable"; case WSAEPROTOTYPE: return "Protocol wrong type for socket";
case WSAEINPROGRESS: case WSAENOPROTOOPT: return "Bad protocol option";
return "WSAEINPROGRESS: Operation now in progress"; case WSAEPROTONOSUPPORT: return "Protocol not supported";
case WSAEALREADY: case WSAESOCKTNOSUPPORT: return "Socket type not supported";
return "WSAEALREADY: Operation already in progress"; case WSAEOPNOTSUPP: return "Operation not supported";
case WSAENOTSOCK: case WSAEPFNOSUPPORT: return "Protocol family not supported";
return "WSAENOTSOCK: Socket operation on nonsocket"; case WSAEAFNOSUPPORT:
case WSAEDESTADDRREQ: return "Address family not supported by protocol family";
return "WSAEDESTADDRREQ: Destination address required"; case WSAEADDRINUSE: return "Address already in use";
case WSAEMSGSIZE: case WSAEADDRNOTAVAIL: return "Cannot assign requested address";
return "WSAEMSGSIZE: Message too long"; case WSAENETDOWN: return "Network is down";
case WSAEPROTOTYPE: case WSAENETUNREACH: return "Network is unreachable";
return "WSAEPROTOTYPE: Protocol wrong type for socket"; case WSAENETRESET: return "Network dropped connection on reset";
case WSAENOPROTOOPT: case WSAECONNABORTED: return "Software caused connection abort";
return "WSAENOPROTOOPT: Bad protocol option"; case WSAECONNRESET: return "Connection reset by peer";
case WSAEPROTONOSUPPORT: case WSAENOBUFS: return "No buffer space available";
return "WSAEPROTONOSUPPORT: Protocol not supported"; case WSAEISCONN: return "Socket is already connected";
case WSAESOCKTNOSUPPORT: case WSAENOTCONN: return "Socket is not connected";
return "WSAESOCKTNOSUPPORT: Socket type not supported"; case WSAESHUTDOWN: return "Cannot send after socket shutdown";
case WSAEOPNOTSUPP: case WSAETIMEDOUT: return "Connection timed out";
return "WSAEOPNOTSUPP: Operation not supported"; case WSAECONNREFUSED: return "Connection refused";
case WSAEPFNOSUPPORT: case WSAEHOSTDOWN: return "Host is down";
return "WSAEPFNOSUPPORT: Protocol family not supported"; case WSAEHOSTUNREACH: return "No route to host";
case WSAEAFNOSUPPORT: case WSAEPROCLIM: return "Too many processes";
return "WSAEAFNOSUPPORT: Address family not supported by " case WSASYSNOTREADY: return "Network subsystem is unavailable";
"protocol family"; case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range";
case WSAEADDRINUSE: case WSANOTINITIALISED:
return "WSAEADDRINUSE: Address already in use"; return "Successful WSAStartup not yet performed";
case WSAEADDRNOTAVAIL: case WSAEDISCON: return "Graceful shutdown in progress";
return "WSAEADDRNOTAVAIL: Cannot assign requested address"; case WSATYPE_NOT_FOUND: return "Class type not found";
case WSAENETDOWN: case WSAHOST_NOT_FOUND: return "Host not found";
return "WSAENETDOWN: Network is down"; case WSATRY_AGAIN: return "Nonauthoritative host not found";
case WSAENETUNREACH: case WSANO_RECOVERY: return "Nonrecoverable name lookup error";
return "WSAENETUNREACH: Network is unreachable"; case WSANO_DATA: return "Valid name, no data record of requested type";
case WSAENETRESET: case WSASYSCALLFAILURE: return "System call failure";
return "WSAENETRESET: Network dropped connection on reset"; default: return "Unknown error";
case WSAECONNABORTED:
return "WSAECONNABORTED: Software caused connection abort";
case WSAECONNRESET:
return "WSAECONNRESET: Connection reset by peer";
case WSAENOBUFS:
return "WSAENOBUFS: No buffer space available";
case WSAEISCONN:
return "WSAEISCONN: Socket is already connected";
case WSAENOTCONN:
return "WSAENOTCONN: Socket is not connected";
case WSAESHUTDOWN:
return "WSAESHUTDOWN: Cannot send after socket shutdown";
case WSAETIMEDOUT:
return "WSAETIMEDOUT: Connection timed out";
case WSAECONNREFUSED:
return "WSAECONNREFUSED: Connection refused";
case WSAEHOSTDOWN:
return "WSAEHOSTDOWN: Host is down";
case WSAEHOSTUNREACH:
return "WSAEHOSTUNREACH: No route to host";
case WSAEPROCLIM:
return "WSAEPROCLIM: Too many processes";
case WSASYSNOTREADY:
return "WSASYSNOTREADY: Network subsystem is unavailable";
case WSAVERNOTSUPPORTED:
return "WSAVERNOTSUPPORTED: Winsock.dll version out of range";
case WSANOTINITIALISED:
return "WSANOTINITIALISED: Successful WSAStartup not yet performed";
case WSAEDISCON:
return "WSAEDISCON: Graceful shutdown in progress";
case WSATYPE_NOT_FOUND:
return "WSATYPE_NOT_FOUND: Class type not found";
case WSAHOST_NOT_FOUND:
return "WSAHOST_NOT_FOUND: Host not found";
case WSATRY_AGAIN:
return "WSATRY_AGAIN: Nonauthoritative host not found";
case WSANO_RECOVERY:
return "WSANO_RECOVERY: Nonrecoverable name lookup error";
case WSANO_DATA:
return "WSANO_DATA: Valid name, no data record of requested type";
case WSASYSCALLFAILURE:
return "WSASYSCALLFAILURE: System call failure";
default:
return "Unknown error";
} }
} }

View File

@ -283,7 +283,9 @@ function empty_connect()
if not data then if not data then
pass("ok") pass("ok")
data = socket.connect(host, port) data = socket.connect(host, port)
else fail("should not have connected!") end else
pass("gethostbyname returns localhost on empty string...")
end
end end
------------------------------------------------------------------------ ------------------------------------------------------------------------
@ -374,19 +376,17 @@ end
------------------------------------------------------------------------ ------------------------------------------------------------------------
function connect_timeout() function connect_timeout()
io.stderr:write("connect with timeout (if it hangs, it failed): ") io.stderr:write("connect with timeout (if it hangs, it failed!): ")
local t = socket.gettime() local t = socket.gettime()
local c, e = socket.tcp() local c, e = socket.tcp()
assert(c, e) assert(c, e)
c:settimeout(0.1) c:settimeout(0.1)
ip = socket.dns.toip("ibere.tecgraf.puc-rio.br")
if not ip then return end
local t = socket.gettime() local t = socket.gettime()
local r, e = c:connect(ip, 80) local r, e = c:connect("127.0.0.2", 80)
assert(not r, "should not connect") assert(not r, "should not connect")
--assert(e == "timeout", e)
assert(socket.gettime() - t < 2, "took too long to give up.") assert(socket.gettime() - t < 2, "took too long to give up.")
c:close() c:close()
print("ok")
end end
------------------------------------------------------------------------ ------------------------------------------------------------------------