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

@ -210,7 +210,7 @@ const char *inet_tryconnect(p_sock ps, const char *address,
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,7 +24,8 @@ 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 */

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,6 +37,35 @@ 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
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
@ -46,7 +74,7 @@ int sock_select(int n, fd_set *rfds, fd_set *wfds, fd_set *efds, p_tm tm) {
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);
/* we run select to wait */
err = sock_select(0, NULL, &wfds, &efds, tm);
/* if select returned due to an event */
if (err > 0) {
/* if was in efds, we failed */
if (FD_ISSET(sock, &efds)) {
int why, len = sizeof(why);
/* give windows time to set the error (disgusting) */
Sleep(0); Sleep(0);
/* find out why we failed */ /* find out why we failed */
getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&why, &len); getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *)&err, &len);
/* we KNOW there was an error. if why is 0, we will return /* we KNOW there was an error. if why is 0, we will return
* "unknown error", but it's not really our fault */ * "unknown error", but it's not really our fault */
return wstrerror(why); return err > 0? err: IO_UNKNOWN;
/* otherwise it must be in wfds, so we succeeded */ /* here we deal with the case in which it worked, timedout or weird errors */
} else return NULL; } else return err;
/* 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;
if (!addr_len) addr_len = &dummy_len;
for ( ;; ) { 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 WSAEDESTADDRREQ:
return "WSAEDESTADDRREQ: Destination address required";
case WSAEMSGSIZE:
return "WSAEMSGSIZE: Message too long";
case WSAEPROTOTYPE:
return "WSAEPROTOTYPE: Protocol wrong type for socket";
case WSAENOPROTOOPT:
return "WSAENOPROTOOPT: Bad protocol option";
case WSAEPROTONOSUPPORT:
return "WSAEPROTONOSUPPORT: Protocol not supported";
case WSAESOCKTNOSUPPORT:
return "WSAESOCKTNOSUPPORT: Socket type not supported";
case WSAEOPNOTSUPP:
return "WSAEOPNOTSUPP: Operation not supported";
case WSAEPFNOSUPPORT:
return "WSAEPFNOSUPPORT: Protocol family not supported";
case WSAEAFNOSUPPORT: case WSAEAFNOSUPPORT:
return "WSAEAFNOSUPPORT: Address family not supported by " return "Address family not supported by protocol family";
"protocol family"; case WSAEADDRINUSE: return "Address already in use";
case WSAEADDRINUSE: case WSAEADDRNOTAVAIL: return "Cannot assign requested address";
return "WSAEADDRINUSE: Address already in use"; case WSAENETDOWN: return "Network is down";
case WSAEADDRNOTAVAIL: case WSAENETUNREACH: return "Network is unreachable";
return "WSAEADDRNOTAVAIL: Cannot assign requested address"; case WSAENETRESET: return "Network dropped connection on reset";
case WSAENETDOWN: case WSAECONNABORTED: return "Software caused connection abort";
return "WSAENETDOWN: Network is down"; case WSAECONNRESET: return "Connection reset by peer";
case WSAENETUNREACH: case WSAENOBUFS: return "No buffer space available";
return "WSAENETUNREACH: Network is unreachable"; case WSAEISCONN: return "Socket is already connected";
case WSAENETRESET: case WSAENOTCONN: return "Socket is not connected";
return "WSAENETRESET: Network dropped connection on reset"; case WSAESHUTDOWN: return "Cannot send after socket shutdown";
case WSAECONNABORTED: case WSAETIMEDOUT: return "Connection timed out";
return "WSAECONNABORTED: Software caused connection abort"; case WSAECONNREFUSED: return "Connection refused";
case WSAECONNRESET: case WSAEHOSTDOWN: return "Host is down";
return "WSAECONNRESET: Connection reset by peer"; case WSAEHOSTUNREACH: return "No route to host";
case WSAENOBUFS: case WSAEPROCLIM: return "Too many processes";
return "WSAENOBUFS: No buffer space available"; case WSASYSNOTREADY: return "Network subsystem is unavailable";
case WSAEISCONN: case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range";
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: case WSANOTINITIALISED:
return "WSANOTINITIALISED: Successful WSAStartup not yet performed"; return "Successful WSAStartup not yet performed";
case WSAEDISCON: case WSAEDISCON: return "Graceful shutdown in progress";
return "WSAEDISCON: Graceful shutdown in progress"; case WSATYPE_NOT_FOUND: return "Class type not found";
case WSATYPE_NOT_FOUND: case WSAHOST_NOT_FOUND: return "Host not found";
return "WSATYPE_NOT_FOUND: Class type not found"; case WSATRY_AGAIN: return "Nonauthoritative host not found";
case WSAHOST_NOT_FOUND: case WSANO_RECOVERY: return "Nonrecoverable name lookup error";
return "WSAHOST_NOT_FOUND: Host not found"; case WSANO_DATA: return "Valid name, no data record of requested type";
case WSATRY_AGAIN: case WSASYSCALLFAILURE: return "System call failure";
return "WSATRY_AGAIN: Nonauthoritative host not found"; default: return "Unknown error";
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
------------------------------------------------------------------------ ------------------------------------------------------------------------