diff --git a/samples/dhparam/params.sh b/samples/dhparam/params.sh old mode 100644 new mode 100755 diff --git a/src/luasocket/buffer.c b/src/luasocket/buffer.c index 0215e59..4ef4e8e 100644 --- a/src/luasocket/buffer.c +++ b/src/luasocket/buffer.c @@ -1,10 +1,6 @@ /*=========================================================================*\ -* LuaSocket 2.0.2 -* Copyright (C) 2004-2007 Diego Nehab -* * Input/Output interface for Lua programs -* -* RCS ID: $Id: buffer.c,v 1.28 2007/06/11 23:44:54 diego Exp $ +* LuaSocket toolkit \*=========================================================================*/ #include "lua.h" #include "lauxlib.h" @@ -32,6 +28,14 @@ static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent); /*=========================================================================*\ * Exported functions \*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int buffer_open(lua_State *L) { + (void) L; + return 0; +} + /*-------------------------------------------------------------------------*\ * Initializes C structure \*-------------------------------------------------------------------------*/ @@ -39,6 +43,29 @@ void buffer_init(p_buffer buf, p_io io, p_timeout tm) { buf->first = buf->last = 0; buf->io = io; buf->tm = tm; + buf->received = buf->sent = 0; + buf->birthday = timeout_gettime(); +} + +/*-------------------------------------------------------------------------*\ +* object:getstats() interface +\*-------------------------------------------------------------------------*/ +int buffer_meth_getstats(lua_State *L, p_buffer buf) { + lua_pushnumber(L, (lua_Number) buf->received); + lua_pushnumber(L, (lua_Number) buf->sent); + lua_pushnumber(L, timeout_gettime() - buf->birthday); + return 3; +} + +/*-------------------------------------------------------------------------*\ +* object:setstats() interface +\*-------------------------------------------------------------------------*/ +int buffer_meth_setstats(lua_State *L, p_buffer buf) { + buf->received = (long) luaL_optnumber(L, 2, (lua_Number) buf->received); + buf->sent = (long) luaL_optnumber(L, 3, (lua_Number) buf->sent); + if (lua_isnumber(L, 4)) buf->birthday = timeout_gettime() - lua_tonumber(L, 4); + lua_pushnumber(L, 1); + return 1; } /*-------------------------------------------------------------------------*\ @@ -51,7 +78,9 @@ int buffer_meth_send(lua_State *L, p_buffer buf) { const char *data = luaL_checklstring(L, 2, &size); long start = (long) luaL_optnumber(L, 3, 1); long end = (long) luaL_optnumber(L, 4, -1); +#ifdef LUASOCKET_DEBUG p_timeout tm = timeout_markstart(buf->tm); +#endif if (start < 0) start = (long) (size+start+1); if (end < 0) end = (long) (size+end+1); if (start < 1) start = (long) 1; @@ -61,9 +90,9 @@ int buffer_meth_send(lua_State *L, p_buffer buf) { if (err != IO_DONE) { lua_pushnil(L); lua_pushstring(L, buf->io->error(buf->io->ctx, err)); - lua_pushnumber(L, sent+start-1); + lua_pushnumber(L, (lua_Number) (sent+start-1)); } else { - lua_pushnumber(L, sent+start-1); + lua_pushnumber(L, (lua_Number) (sent+start-1)); lua_pushnil(L); lua_pushnil(L); } @@ -82,7 +111,9 @@ int buffer_meth_receive(lua_State *L, p_buffer buf) { luaL_Buffer b; size_t size; const char *part = luaL_optlstring(L, 3, "", &size); +#ifdef LUASOCKET_DEBUG p_timeout tm = timeout_markstart(buf->tm); +#endif /* initialize buffer with optional extra prefix * (useful for concatenating previous partial results) */ luaL_buffinit(L, &b); @@ -93,9 +124,15 @@ int buffer_meth_receive(lua_State *L, p_buffer buf) { if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b); else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b); else luaL_argcheck(L, 0, 2, "invalid receive pattern"); - /* get a fixed number of bytes (minus what was already partially - * received) */ - } else err = recvraw(buf, (size_t) lua_tonumber(L, 2)-size, &b); + /* get a fixed number of bytes (minus what was already partially + * received) */ + } else { + double n = lua_tonumber(L, 2); + size_t wanted = (size_t) n; + luaL_argcheck(L, n >= 0, 2, "invalid receive pattern"); + if (size == 0 || wanted > size) + err = recvraw(buf, wanted-size, &b); + } /* check if there was an error */ if (err != IO_DONE) { /* we can't push anyting in the stack before pushing the @@ -137,12 +174,13 @@ static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent) { size_t total = 0; int err = IO_DONE; while (total < count && err == IO_DONE) { - size_t done; + size_t done = 0; size_t step = (count-total <= STEPSIZE)? count-total: STEPSIZE; err = io->send(io->ctx, data+total, step, &done, tm); total += done; } *sent = total; + buf->sent += total; return err; } @@ -212,6 +250,7 @@ static int recvline(p_buffer buf, luaL_Buffer *b) { * transport layer \*-------------------------------------------------------------------------*/ static void buffer_skip(p_buffer buf, size_t count) { + buf->received += count; buf->first += count; if (buffer_isempty(buf)) buf->first = buf->last = 0; diff --git a/src/luasocket/buffer.h b/src/luasocket/buffer.h index 61d2798..1281bb3 100644 --- a/src/luasocket/buffer.h +++ b/src/luasocket/buffer.h @@ -1,10 +1,8 @@ #ifndef BUF_H #define BUF_H /*=========================================================================*\ -* LuaSocket 2.0.2 -* Copyright (C) 2004-2007 Diego Nehab -* * Input/Output interface for Lua programs +* LuaSocket toolkit * * Line patterns require buffering. Reading one character at a time involves * too many system calls and is very slow. This module implements the @@ -16,11 +14,8 @@ * * The module is built on top of the I/O abstraction defined in io.h and the * timeout management is done with the timeout.h interface. -* -* -* RCS ID: $Id: buffer.h,v 1.12 2005/10/07 04:40:59 diego Exp $ \*=========================================================================*/ -#include +#include "lua.h" #include "io.h" #include "timeout.h" @@ -30,6 +25,8 @@ /* buffer control structure */ typedef struct t_buffer_ { + double birthday; /* throttle support info: creation time, */ + size_t sent, received; /* bytes sent, and bytes received */ p_io io; /* IO driver used for this buffer */ p_timeout tm; /* timeout management for this buffer */ size_t first, last; /* index of first and last bytes of stored data */ @@ -37,9 +34,12 @@ typedef struct t_buffer_ { } t_buffer; typedef t_buffer *p_buffer; +int buffer_open(lua_State *L); void buffer_init(p_buffer buf, p_io io, p_timeout tm); int buffer_meth_send(lua_State *L, p_buffer buf); int buffer_meth_receive(lua_State *L, p_buffer buf); +int buffer_meth_getstats(lua_State *L, p_buffer buf); +int buffer_meth_setstats(lua_State *L, p_buffer buf); int buffer_isempty(p_buffer buf); #endif /* BUF_H */ diff --git a/src/luasocket/io.c b/src/luasocket/io.c index ff2e7ad..35f46f7 100644 --- a/src/luasocket/io.c +++ b/src/luasocket/io.c @@ -1,10 +1,6 @@ /*=========================================================================*\ -* LuaSocket 2.0.2 -* Copyright (C) 2004-2007 Diego Nehab -* * Input/Output abstraction -* -* RCS ID: $Id: io.c 2 2006-04-30 19:30:47Z brunoos $ +* LuaSocket toolkit \*=========================================================================*/ #include "io.h" diff --git a/src/luasocket/io.h b/src/luasocket/io.h index b5942d3..76a3e58 100644 --- a/src/luasocket/io.h +++ b/src/luasocket/io.h @@ -1,10 +1,8 @@ #ifndef IO_H #define IO_H /*=========================================================================*\ -* LuaSocket 2.0.2 -* Copyright (C) 2004-2007 Diego Nehab -* * Input/Output abstraction +* LuaSocket toolkit * * This module defines the interface that LuaSocket expects from the * transport layer for streamed input/output. The idea is that if any @@ -13,21 +11,18 @@ * * The module socket.h implements this interface, and thus the module tcp.h * is very simple. -* -* RCS ID: $Id: io.h 6 2006-04-30 20:33:05Z brunoos $ \*=========================================================================*/ #include -#include +#include "lua.h" #include "timeout.h" /* IO error codes */ enum { - IO_DONE = 0, /* operation completed successfully */ - IO_TIMEOUT = -1, /* operation timed out */ - IO_CLOSED = -2, /* the connection has been closed */ - IO_UNKNOWN = -3, /* Unknown error */ - IO_SSL = -4 /* SSL error */ + IO_DONE = 0, /* operation completed successfully */ + IO_TIMEOUT = -1, /* operation timed out */ + IO_CLOSED = -2, /* the connection has been closed */ + IO_UNKNOWN = -3 }; /* interface to error message function */ diff --git a/src/luasocket/socket.h b/src/luasocket/socket.h index 8a5b1f2..63573de 100644 --- a/src/luasocket/socket.h +++ b/src/luasocket/socket.h @@ -1,17 +1,13 @@ #ifndef SOCKET_H #define SOCKET_H /*=========================================================================*\ -* LuaSocket 2.0.2 -* Copyright (C) 2004-2007 Diego Nehab -* * Socket compatibilization module +* LuaSocket toolkit * * BSD Sockets and WinSock are similar, but there are a few irritating * differences. Also, not all *nix platforms behave the same. This module * (and the associated usocket.h and wsocket.h) factor these differences and * creates a interface compatible with the io.h module. -* -* RCS ID: $Id: socket.h 2 2006-04-30 19:30:47Z brunoos $ \*=========================================================================*/ #include "io.h" @@ -32,6 +28,9 @@ \*=========================================================================*/ #include "timeout.h" +/* we are lazy... */ +typedef struct sockaddr SA; + /*=========================================================================*\ * Functions bellow implement a comfortable platform independent * interface to sockets @@ -39,9 +38,41 @@ int socket_open(void); int socket_close(void); void socket_destroy(p_socket ps); +void socket_shutdown(p_socket ps, int how); +int socket_sendto(p_socket ps, const char *data, size_t count, + size_t *sent, SA *addr, socklen_t addr_len, p_timeout tm); +int socket_recvfrom(p_socket ps, char *data, size_t count, + size_t *got, SA *addr, socklen_t *addr_len, p_timeout tm); + void socket_setnonblocking(p_socket ps); void socket_setblocking(p_socket ps); + int socket_waitfd(p_socket ps, int sw, p_timeout tm); +int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, + p_timeout tm); + +int socket_connect(p_socket ps, SA *addr, socklen_t addr_len, p_timeout tm); +int socket_create(p_socket ps, int domain, int type, int protocol); +int socket_bind(p_socket ps, SA *addr, socklen_t addr_len); +int socket_listen(p_socket ps, int backlog); +int socket_accept(p_socket ps, p_socket pa, SA *addr, + socklen_t *addr_len, p_timeout tm); + +const char *socket_hoststrerror(int err); +const char *socket_gaistrerror(int err); const char *socket_strerror(int err); +/* these are perfect to use with the io abstraction module + and the buffered input module */ +int socket_send(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm); +int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm); +int socket_write(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm); +int socket_read(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm); +const char *socket_ioerror(p_socket ps, int err); + +int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp); +int socket_gethostbyname(const char *addr, struct hostent **hp); + #endif /* SOCKET_H */ diff --git a/src/luasocket/timeout.c b/src/luasocket/timeout.c index d141213..94a524b 100644 --- a/src/luasocket/timeout.c +++ b/src/luasocket/timeout.c @@ -1,12 +1,15 @@ /*=========================================================================*\ -* LuaSocket 2.0.2 -* Copyright (C) 2004-2007 Diego Nehab -* * Timeout management functions -* -* RCS ID: $Id: timeout.c,v 1.30 2005/10/07 04:40:59 diego Exp $ +* LuaSocket toolkit \*=========================================================================*/ #include +#include +#include + +#include "lua.h" +#include "lauxlib.h" + +#include "timeout.h" #ifdef _WIN32 #include @@ -15,11 +18,6 @@ #include #endif -#include -#include - -#include "timeout.h" - /* min and max macros */ #ifndef MIN #define MIN(x, y) ((x) < (y) ? x : y) @@ -28,6 +26,18 @@ #define MAX(x, y) ((x) > (y) ? x : y) #endif +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int timeout_lua_gettime(lua_State *L); +static int timeout_lua_sleep(lua_State *L); + +static luaL_Reg func[] = { + { "gettime", timeout_lua_gettime }, + { "sleep", timeout_lua_sleep }, + { NULL, NULL } +}; + /*=========================================================================*\ * Exported functions. \*=========================================================================*/ @@ -129,6 +139,18 @@ double timeout_gettime(void) { } #endif +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int timeout_open(lua_State *L) { +#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE) + luaL_setfuncs(L, func, 0); +#else + luaL_openlib(L, NULL, func, 0); +#endif + return 0; +} + /*-------------------------------------------------------------------------*\ * Sets timeout values for IO operations * Lua Input: base, time [, mode] @@ -153,3 +175,46 @@ int timeout_meth_settimeout(lua_State *L, p_timeout tm) { return 1; } +/*=========================================================================*\ +* Test support functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Returns the time the system has been up, in secconds. +\*-------------------------------------------------------------------------*/ +static int timeout_lua_gettime(lua_State *L) +{ + lua_pushnumber(L, timeout_gettime()); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Sleep for n seconds. +\*-------------------------------------------------------------------------*/ +#ifdef _WIN32 +int timeout_lua_sleep(lua_State *L) +{ + double n = luaL_checknumber(L, 1); + if (n < 0.0) n = 0.0; + if (n < DBL_MAX/1000.0) n *= 1000.0; + if (n > INT_MAX) n = INT_MAX; + Sleep((int)n); + return 0; +} +#else +int timeout_lua_sleep(lua_State *L) +{ + double n = luaL_checknumber(L, 1); + struct timespec t, r; + if (n < 0.0) n = 0.0; + if (n > INT_MAX) n = INT_MAX; + t.tv_sec = (int) n; + n -= t.tv_sec; + t.tv_nsec = (int) (n * 1000000000); + if (t.tv_nsec >= 1000000000) t.tv_nsec = 999999999; + while (nanosleep(&t, &r) != 0) { + t.tv_sec = r.tv_sec; + t.tv_nsec = r.tv_nsec; + } + return 0; +} +#endif diff --git a/src/luasocket/timeout.h b/src/luasocket/timeout.h index 1157d43..6715ca7 100644 --- a/src/luasocket/timeout.h +++ b/src/luasocket/timeout.h @@ -1,14 +1,10 @@ #ifndef TIMEOUT_H #define TIMEOUT_H /*=========================================================================*\ -* LuaSocket 2.0.2 -* Copyright (C) 2004-2007 Diego Nehab -* * Timeout management functions -* -* RCS ID: $Id: timeout.h 2 2006-04-30 19:30:47Z brunoos $ +* LuaSocket toolkit \*=========================================================================*/ -#include +#include "lua.h" /* timeout control structure */ typedef struct t_timeout_ { diff --git a/src/luasocket/usocket.c b/src/luasocket/usocket.c index 21c1c36..da09130 100644 --- a/src/luasocket/usocket.c +++ b/src/luasocket/usocket.c @@ -1,31 +1,20 @@ /*=========================================================================*\ -* LuaSocket 2.0.2 -* Copyright (C) 2004-2007 Diego Nehab -* * Socket compatibilization module for Unix +* LuaSocket toolkit * * 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. -* -* RCS ID: $Id: usocket.c,v 1.38 2007/10/13 23:55:20 diego Exp $ \*=========================================================================*/ -#include -#include -#include -#include #include #include -#include -#include #include "socket.h" -#include "usocket.h" /*-------------------------------------------------------------------------*\ * Wait for readable/writable/connected socket with timeout \*-------------------------------------------------------------------------*/ -#ifdef SOCKET_POLL +#ifndef SOCKET_SELECT int socket_waitfd(p_socket ps, int sw, p_timeout tm) { int ret; struct pollfd pfd; @@ -48,6 +37,7 @@ int socket_waitfd(p_socket ps, int sw, p_timeout tm) { fd_set rfds, wfds, *rp, *wp; struct timeval tv, *tp; double t; + if (*ps >= FD_SETSIZE) return EINVAL; if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ do { /* must set bits within loop, because select may have modifed them */ @@ -98,6 +88,263 @@ void socket_destroy(p_socket ps) { } } +/*-------------------------------------------------------------------------*\ +* Select with timeout control +\*-------------------------------------------------------------------------*/ +int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, + p_timeout tm) { + int ret; + do { + struct timeval tv; + double t = timeout_getretry(tm); + tv.tv_sec = (int) t; + tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6); + /* timeout = 0 means no wait */ + ret = select(n, rfds, wfds, efds, t >= 0.0 ? &tv: NULL); + } while (ret < 0 && errno == EINTR); + return ret; +} + +/*-------------------------------------------------------------------------*\ +* Creates and sets up a socket +\*-------------------------------------------------------------------------*/ +int socket_create(p_socket ps, int domain, int type, int protocol) { + *ps = socket(domain, type, protocol); + if (*ps != SOCKET_INVALID) return IO_DONE; + else return errno; +} + +/*-------------------------------------------------------------------------*\ +* Binds or returns error message +\*-------------------------------------------------------------------------*/ +int socket_bind(p_socket ps, SA *addr, socklen_t len) { + int err = IO_DONE; + socket_setblocking(ps); + if (bind(*ps, addr, len) < 0) err = errno; + socket_setnonblocking(ps); + return err; +} + +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +int socket_listen(p_socket ps, int backlog) { + int err = IO_DONE; + socket_setblocking(ps); + if (listen(*ps, backlog)) err = errno; + socket_setnonblocking(ps); + return err; +} + +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +void socket_shutdown(p_socket ps, int how) { + socket_setblocking(ps); + shutdown(*ps, how); + socket_setnonblocking(ps); +} + +/*-------------------------------------------------------------------------*\ +* Connects or returns error message +\*-------------------------------------------------------------------------*/ +int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) { + int err; + /* avoid calling on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* call connect until done or failed without being interrupted */ + do if (connect(*ps, addr, len) == 0) return IO_DONE; + while ((err = errno) == EINTR); + /* if connection failed immediately, return error code */ + if (err != EINPROGRESS && err != EAGAIN) return err; + /* zero timeout case optimization */ + if (timeout_iszero(tm)) return IO_TIMEOUT; + /* wait until we have the result of the connection attempt or timeout */ + err = socket_waitfd(ps, WAITFD_C, tm); + if (err == IO_CLOSED) { + if (recv(*ps, (char *) &err, 0, 0) == 0) return IO_DONE; + else return errno; + } else return err; +} + +/*-------------------------------------------------------------------------*\ +* Accept with timeout +\*-------------------------------------------------------------------------*/ +int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, p_timeout tm) { + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int err; + if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE; + err = errno; + if (err == EINTR) continue; + if (err != EAGAIN && err != ECONNABORTED) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + /* can't reach here */ + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Send with timeout +\*-------------------------------------------------------------------------*/ +int socket_send(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm) +{ + int err; + *sent = 0; + /* avoid making system calls on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* loop until we send something or we give up on error */ + for ( ;; ) { + long put = (long) send(*ps, data, count, 0); + /* if we sent anything, we are done */ + if (put >= 0) { + *sent = put; + return IO_DONE; + } + err = errno; + /* EPIPE means the connection was closed */ + if (err == EPIPE) return IO_CLOSED; + /* we call was interrupted, just try again */ + if (err == EINTR) continue; + /* if failed fatal reason, report error */ + if (err != EAGAIN) return err; + /* wait until we can send something or we timeout */ + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } + /* can't reach here */ + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Sendto with timeout +\*-------------------------------------------------------------------------*/ +int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, + SA *addr, socklen_t len, p_timeout tm) +{ + int err; + *sent = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + long put = (long) sendto(*ps, data, count, 0, addr, len); + if (put >= 0) { + *sent = put; + return IO_DONE; + } + err = errno; + if (err == EPIPE) return IO_CLOSED; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Receive with timeout +\*-------------------------------------------------------------------------*/ +int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) { + int err; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + long taken = (long) recv(*ps, data, count, 0); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + err = errno; + if (taken == 0) return IO_CLOSED; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Recvfrom with timeout +\*-------------------------------------------------------------------------*/ +int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, + SA *addr, socklen_t *len, p_timeout tm) { + int err; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + long taken = (long) recvfrom(*ps, data, count, 0, addr, len); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + err = errno; + if (taken == 0) return IO_CLOSED; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + + +/*-------------------------------------------------------------------------*\ +* Write with timeout +* +* socket_read and socket_write are cut-n-paste of socket_send and socket_recv, +* with send/recv replaced with write/read. We can't just use write/read +* in the socket version, because behaviour when size is zero is different. +\*-------------------------------------------------------------------------*/ +int socket_write(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm) +{ + int err; + *sent = 0; + /* avoid making system calls on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* loop until we send something or we give up on error */ + for ( ;; ) { + long put = (long) write(*ps, data, count); + /* if we sent anything, we are done */ + if (put >= 0) { + *sent = put; + return IO_DONE; + } + err = errno; + /* EPIPE means the connection was closed */ + if (err == EPIPE) return IO_CLOSED; + /* we call was interrupted, just try again */ + if (err == EINTR) continue; + /* if failed fatal reason, report error */ + if (err != EAGAIN) return err; + /* wait until we can send something or we timeout */ + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } + /* can't reach here */ + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Read with timeout +* See note for socket_write +\*-------------------------------------------------------------------------*/ +int socket_read(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) { + int err; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + long taken = (long) read(*ps, data, count); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + err = errno; + if (taken == 0) return IO_CLOSED; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + /*-------------------------------------------------------------------------*\ * Put socket into blocking mode \*-------------------------------------------------------------------------*/ @@ -116,10 +363,37 @@ void socket_setnonblocking(p_socket ps) { fcntl(*ps, F_SETFL, flags); } +/*-------------------------------------------------------------------------*\ +* DNS helpers +\*-------------------------------------------------------------------------*/ +int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) { + *hp = gethostbyaddr(addr, len, AF_INET); + if (*hp) return IO_DONE; + else if (h_errno) return h_errno; + else if (errno) return errno; + else return IO_UNKNOWN; +} + +int socket_gethostbyname(const char *addr, struct hostent **hp) { + *hp = gethostbyname(addr); + if (*hp) return IO_DONE; + else if (h_errno) return h_errno; + else if (errno) return errno; + else return IO_UNKNOWN; +} + /*-------------------------------------------------------------------------*\ * Error translation functions * Make sure important error messages are standard \*-------------------------------------------------------------------------*/ +const char *socket_hoststrerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case HOST_NOT_FOUND: return "host not found"; + default: return hstrerror(err); + } +} + const char *socket_strerror(int err) { if (err <= 0) return io_strerror(err); switch (err) { @@ -129,8 +403,37 @@ const char *socket_strerror(int err) { case ECONNREFUSED: return "connection refused"; case ECONNABORTED: return "closed"; case ECONNRESET: return "closed"; - case EPIPE: return "closed"; case ETIMEDOUT: return "timeout"; - default: return strerror(errno); + default: return strerror(err); } } + +const char *socket_ioerror(p_socket ps, int err) { + (void) ps; + return socket_strerror(err); +} + +const char *socket_gaistrerror(int err) { + if (err == 0) return NULL; + switch (err) { + case EAI_AGAIN: return "temporary failure in name resolution"; + case EAI_BADFLAGS: return "invalid value for ai_flags"; +#ifdef EAI_BADHINTS + case EAI_BADHINTS: return "invalid value for hints"; +#endif + case EAI_FAIL: return "non-recoverable failure in name resolution"; + case EAI_FAMILY: return "ai_family not supported"; + case EAI_MEMORY: return "memory allocation failure"; + case EAI_NONAME: + return "host or service not provided, or not known"; + case EAI_OVERFLOW: return "argument buffer overflow"; +#ifdef EAI_PROTOCOL + case EAI_PROTOCOL: return "resolved protocol is unknown"; +#endif + case EAI_SERVICE: return "service not supported for socket type"; + case EAI_SOCKTYPE: return "ai_socktype not supported"; + case EAI_SYSTEM: return strerror(errno); + default: return gai_strerror(err); + } +} + diff --git a/src/luasocket/usocket.h b/src/luasocket/usocket.h index de8a4a9..ecbcd8e 100644 --- a/src/luasocket/usocket.h +++ b/src/luasocket/usocket.h @@ -1,15 +1,37 @@ #ifndef USOCKET_H #define USOCKET_H /*=========================================================================*\ -* LuaSocket 2.0.2 -* Copyright (C) 2004-2007 Diego Nehab -* * Socket compatibilization module for Unix -* -* RCS ID: $Id: usocket.h 6 2006-04-30 20:33:05Z brunoos $ +* LuaSocket toolkit \*=========================================================================*/ -#ifdef SOCKET_POLL +/*=========================================================================*\ +* BSD include files +\*=========================================================================*/ +/* error codes */ +#include +/* close function */ +#include +/* fnctnl function and associated constants */ +#include +/* struct sockaddr */ +#include +/* socket function */ +#include +/* struct timeval */ +#include +/* gethostbyname and gethostbyaddr functions */ +#include +/* sigpipe handling */ +#include +/* IP stuff*/ +#include +#include +/* TCP options (nagle algorithm disable) */ +#include +#include + +#ifndef SOCKET_SELECT #include #define WAITFD_R POLLIN #define WAITFD_W POLLOUT @@ -20,8 +42,28 @@ #define WAITFD_C (WAITFD_R|WAITFD_W) #endif +#ifndef SO_REUSEPORT +#define SO_REUSEPORT SO_REUSEADDR +#endif + +/* Some platforms use IPV6_JOIN_GROUP instead if + * IPV6_ADD_MEMBERSHIP. The semantics are same, though. */ +#ifndef IPV6_ADD_MEMBERSHIP +#ifdef IPV6_JOIN_GROUP +#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +#endif /* IPV6_JOIN_GROUP */ +#endif /* !IPV6_ADD_MEMBERSHIP */ + +/* Same with IPV6_DROP_MEMBERSHIP / IPV6_LEAVE_GROUP. */ +#ifndef IPV6_DROP_MEMBERSHIP +#ifdef IPV6_LEAVE_GROUP +#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP +#endif /* IPV6_LEAVE_GROUP */ +#endif /* !IPV6_DROP_MEMBERSHIP */ + typedef int t_socket; typedef t_socket *p_socket; +typedef struct sockaddr_storage t_sockaddr_storage; #define SOCKET_INVALID (-1) diff --git a/src/luasocket/wsocket.c b/src/luasocket/wsocket.c index c187ee9..8c7640e 100644 --- a/src/luasocket/wsocket.c +++ b/src/luasocket/wsocket.c @@ -1,18 +1,17 @@ /*=========================================================================*\ -* LuaSocket 2.0.2 -* Copyright (C) 2004-2007 Diego Nehab -* * Socket compatibilization module for Win32 +* LuaSocket toolkit * * The penalty of calling select to avoid busy-wait is only paid when * the I/O call fail in the first place. -* -* RCS ID: $Id: wsocket.c,v 1.36 2007/06/11 23:44:54 diego Exp $ \*=========================================================================*/ #include #include "socket.h" +/* WinSock doesn't have a strerror... */ +static const char *wstrerror(int err); + /*-------------------------------------------------------------------------*\ * Initializes module \*-------------------------------------------------------------------------*/ @@ -48,7 +47,7 @@ int socket_waitfd(p_socket ps, int sw, p_timeout tm) { if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ if (sw & WAITFD_R) { FD_ZERO(&rfds); - FD_SET(*ps, &rfds); + FD_SET(*ps, &rfds); rp = &rfds; } if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; } @@ -65,6 +64,21 @@ int socket_waitfd(p_socket ps, int sw, p_timeout tm) { return IO_DONE; } +/*-------------------------------------------------------------------------*\ +* Select with int timeout in ms +\*-------------------------------------------------------------------------*/ +int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, + p_timeout tm) { + struct timeval tv; + double t = timeout_get(tm); + tv.tv_sec = (int) t; + tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6); + if (n <= 0) { + Sleep((DWORD) (1000*t)); + return 0; + } else return select(0, rfds, wfds, efds, t >= 0.0? &tv: NULL); +} + /*-------------------------------------------------------------------------*\ * Close and inutilize socket \*-------------------------------------------------------------------------*/ @@ -76,6 +90,204 @@ void socket_destroy(p_socket ps) { } } +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +void socket_shutdown(p_socket ps, int how) { + socket_setblocking(ps); + shutdown(*ps, how); + socket_setnonblocking(ps); +} + +/*-------------------------------------------------------------------------*\ +* Creates and sets up a socket +\*-------------------------------------------------------------------------*/ +int socket_create(p_socket ps, int domain, int type, int protocol) { + *ps = socket(domain, type, protocol); + if (*ps != SOCKET_INVALID) return IO_DONE; + else return WSAGetLastError(); +} + +/*-------------------------------------------------------------------------*\ +* Connects or returns error message +\*-------------------------------------------------------------------------*/ +int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) { + int err; + /* don't call on closed socket */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* ask system to connect */ + if (connect(*ps, addr, len) == 0) return IO_DONE; + /* make sure the system is trying to connect */ + err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK && err != WSAEINPROGRESS) return err; + /* zero timeout case optimization */ + if (timeout_iszero(tm)) return IO_TIMEOUT; + /* we wait until something happens */ + err = socket_waitfd(ps, WAITFD_C, tm); + if (err == IO_CLOSED) { + int len = sizeof(err); + /* give windows time to set the error (yes, disgusting) */ + Sleep(10); + /* find out why we failed */ + getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *)&err, &len); + /* we KNOW there was an error. if 'why' is 0, we will return + * "unknown error", but it's not really our fault */ + return err > 0? err: IO_UNKNOWN; + } else return err; + +} + +/*-------------------------------------------------------------------------*\ +* Binds or returns error message +\*-------------------------------------------------------------------------*/ +int socket_bind(p_socket ps, SA *addr, socklen_t len) { + int err = IO_DONE; + socket_setblocking(ps); + if (bind(*ps, addr, len) < 0) err = WSAGetLastError(); + socket_setnonblocking(ps); + return err; +} + +/*-------------------------------------------------------------------------*\ +* +\*-------------------------------------------------------------------------*/ +int socket_listen(p_socket ps, int backlog) { + int err = IO_DONE; + socket_setblocking(ps); + if (listen(*ps, backlog) < 0) err = WSAGetLastError(); + socket_setnonblocking(ps); + return err; +} + +/*-------------------------------------------------------------------------*\ +* Accept with timeout +\*-------------------------------------------------------------------------*/ +int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, + p_timeout tm) { + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int err; + /* try to get client socket */ + if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE; + /* find out why we failed */ + err = WSAGetLastError(); + /* if we failed because there was no connectoin, keep trying */ + if (err != WSAEWOULDBLOCK && err != WSAECONNABORTED) return err; + /* call select to avoid busy wait */ + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Send with timeout +* On windows, if you try to send 10MB, the OS will buffer EVERYTHING +* this can take an awful lot of time and we will end up blocked. +* Therefore, whoever calls this function should not pass a huge buffer. +\*-------------------------------------------------------------------------*/ +int socket_send(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm) +{ + int err; + *sent = 0; + /* avoid making system calls on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* loop until we send something or we give up on error */ + for ( ;; ) { + /* try to send something */ + int put = send(*ps, data, (int) count, 0); + /* if we sent something, we are done */ + if (put > 0) { + *sent = put; + return IO_DONE; + } + /* deal with failure */ + err = WSAGetLastError(); + /* we can only proceed if there was no serious error */ + if (err != WSAEWOULDBLOCK) return err; + /* avoid busy wait */ + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Sendto with timeout +\*-------------------------------------------------------------------------*/ +int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, + SA *addr, socklen_t len, p_timeout tm) +{ + int err; + *sent = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int put = sendto(*ps, data, (int) count, 0, addr, len); + if (put > 0) { + *sent = put; + return IO_DONE; + } + err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK) return err; + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Receive with timeout +\*-------------------------------------------------------------------------*/ +int socket_recv(p_socket ps, char *data, size_t count, size_t *got, + p_timeout tm) +{ + int err, prev = IO_DONE; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int taken = recv(*ps, data, (int) count, 0); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + if (taken == 0) return IO_CLOSED; + err = WSAGetLastError(); + /* On UDP, a connreset simply means the previous send failed. + * So we try again. + * On TCP, it means our socket is now useless, so the error passes. + * (We will loop again, exiting because the same error will happen) */ + if (err != WSAEWOULDBLOCK) { + if (err != WSAECONNRESET || prev == WSAECONNRESET) return err; + prev = err; + } + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } +} + +/*-------------------------------------------------------------------------*\ +* Recvfrom with timeout +\*-------------------------------------------------------------------------*/ +int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, + SA *addr, socklen_t *len, p_timeout tm) +{ + int err, prev = IO_DONE; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int taken = recvfrom(*ps, data, (int) count, 0, addr, len); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + if (taken == 0) return IO_CLOSED; + err = WSAGetLastError(); + /* On UDP, a connreset simply means the previous send failed. + * So we try again. + * On TCP, it means our socket is now useless, so the error passes. + * (We will loop again, exiting because the same error will happen) */ + if (err != WSAEWOULDBLOCK) { + if (err != WSAECONNRESET || prev == WSAECONNRESET) return err; + prev = err; + } + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } +} + /*-------------------------------------------------------------------------*\ * Put socket into blocking mode \*-------------------------------------------------------------------------*/ @@ -92,11 +304,51 @@ void socket_setnonblocking(p_socket ps) { ioctlsocket(*ps, FIONBIO, &argp); } +/*-------------------------------------------------------------------------*\ +* DNS helpers +\*-------------------------------------------------------------------------*/ +int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) { + *hp = gethostbyaddr(addr, len, AF_INET); + if (*hp) return IO_DONE; + else return WSAGetLastError(); +} + +int socket_gethostbyname(const char *addr, struct hostent **hp) { + *hp = gethostbyname(addr); + if (*hp) return IO_DONE; + else return WSAGetLastError(); +} + /*-------------------------------------------------------------------------*\ * Error translation functions \*-------------------------------------------------------------------------*/ +const char *socket_hoststrerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case WSAHOST_NOT_FOUND: return "host not found"; + default: return wstrerror(err); + } +} + +const char *socket_strerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { + case WSAEADDRINUSE: return "address already in use"; + case WSAECONNREFUSED: return "connection refused"; + case WSAEISCONN: return "already connected"; + case WSAEACCES: return "permission denied"; + case WSAECONNABORTED: return "closed"; + case WSAECONNRESET: return "closed"; + case WSAETIMEDOUT: return "timeout"; + default: return wstrerror(err); + } +} + +const char *socket_ioerror(p_socket ps, int err) { + (void) ps; + return socket_strerror(err); +} -/* WinSock doesn't have a strerror... */ static const char *wstrerror(int err) { switch (err) { case WSAEINTR: return "Interrupted function call"; @@ -147,16 +399,31 @@ static const char *wstrerror(int err) { } } -const char *socket_strerror(int err) { - if (err <= 0) return io_strerror(err); +const char *socket_gaistrerror(int err) { + if (err == 0) return NULL; switch (err) { - case WSAEADDRINUSE: return "address already in use"; - case WSAECONNREFUSED: return "connection refused"; - case WSAEISCONN: return "already connected"; - case WSAEACCES: return "permission denied"; - case WSAECONNABORTED: return "closed"; - case WSAECONNRESET: return "closed"; - case WSAETIMEDOUT: return "timeout"; - default: return wstrerror(err); + case EAI_AGAIN: return "temporary failure in name resolution"; + case EAI_BADFLAGS: return "invalid value for ai_flags"; +#ifdef EAI_BADHINTS + case EAI_BADHINTS: return "invalid value for hints"; +#endif + case EAI_FAIL: return "non-recoverable failure in name resolution"; + case EAI_FAMILY: return "ai_family not supported"; + case EAI_MEMORY: return "memory allocation failure"; + case EAI_NONAME: + return "host or service not provided, or not known"; +#ifdef EAI_OVERFLOW + case EAI_OVERFLOW: return "argument buffer overflow"; +#endif +#ifdef EAI_PROTOCOL + case EAI_PROTOCOL: return "resolved protocol is unknown"; +#endif + case EAI_SERVICE: return "service not supported for socket type"; + case EAI_SOCKTYPE: return "ai_socktype not supported"; +#ifdef EAI_SYSTEM + case EAI_SYSTEM: return strerror(errno); +#endif + default: return gai_strerror(err); } } + diff --git a/src/luasocket/wsocket.h b/src/luasocket/wsocket.h index b977df6..c5a4b1c 100644 --- a/src/luasocket/wsocket.h +++ b/src/luasocket/wsocket.h @@ -1,28 +1,38 @@ #ifndef WSOCKET_H #define WSOCKET_H /*=========================================================================*\ -* LuaSocket 2.0.2 -* Copyright (C) 2004-2007 Diego Nehab -* * Socket compatibilization module for Win32 -* -* RCS ID: $Id: wsocket.h 2 2006-04-30 19:30:47Z brunoos $ +* LuaSocket toolkit \*=========================================================================*/ /*=========================================================================*\ * WinSock include files \*=========================================================================*/ -#include +#include +#include + +typedef int socklen_t; +typedef SOCKADDR_STORAGE t_sockaddr_storage; +typedef SOCKET t_socket; +typedef t_socket *p_socket; #define WAITFD_R 1 #define WAITFD_W 2 #define WAITFD_E 4 #define WAITFD_C (WAITFD_E|WAITFD_W) +#ifndef IPV6_V6ONLY +#define IPV6_V6ONLY 27 +#endif + #define SOCKET_INVALID (INVALID_SOCKET) -typedef int socklen_t; -typedef SOCKET t_socket; -typedef t_socket *p_socket; +#ifndef SO_REUSEPORT +#define SO_REUSEPORT SO_REUSEADDR +#endif + +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV (0) +#endif #endif /* WSOCKET_H */ diff --git a/src/ssl.c b/src/ssl.c index 8273296..dd9f6c7 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -286,6 +286,22 @@ static int meth_receive(lua_State *L) { return buffer_meth_receive(L, &ssl->buf); } +/** + * Get the buffer's statistics. + */ +static int meth_getstats(lua_State *L) { + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + return buffer_meth_getstats(L, &ssl->buf); +} + +/** + * Set the buffer's statistics. + */ +static int meth_setstats(lua_State *L) { + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection"); + return buffer_meth_setstats(L, &ssl->buf); +} + /** * Select support methods */ @@ -655,6 +671,8 @@ static luaL_Reg methods[] = { {"getpeerchain", meth_getpeerchain}, {"getpeerverification", meth_getpeerverification}, {"getpeerfinished", meth_getpeerfinished}, + {"getstats", meth_getstats}, + {"setstats", meth_setstats}, {"dirty", meth_dirty}, {"dohandshake", meth_handshake}, {"receive", meth_receive},