diff --git a/TODO b/TODO index b24f9e5..835929f 100644 --- a/TODO +++ b/TODO @@ -22,6 +22,7 @@ tests trust character constants in mime.c? noooooo. smtp.lua needs stuff filter +add comments into each C module. new option.c module to put all options (TCP and UDP share...)? testar os options! add _tostring methods! diff --git a/src/auxiliar.c b/src/auxiliar.c index 4b3a0f6..6888f9c 100644 --- a/src/auxiliar.c +++ b/src/auxiliar.c @@ -65,26 +65,6 @@ int aux_checkboolean(lua_State *L, int objidx) return lua_toboolean(L, objidx); } -/*-------------------------------------------------------------------------*\ -* Calls appropriate option handler -\*-------------------------------------------------------------------------*/ -int aux_meth_setoption(lua_State *L, luaL_reg *opt) -{ - const char *name = luaL_checkstring(L, 2); /* obj, name, args */ - while (opt->name && strcmp(name, opt->name)) - opt++; - if (!opt->func) { - char msg[45]; - sprintf(msg, "unknown option `%.35s'", name); - luaL_argerror(L, 2, msg); - } - lua_remove(L, 2); /* obj, args */ - lua_pushcfunction(L, opt->func); /* obj, args, func */ - lua_insert(L, 1); /* func, obj, args */ - lua_call(L, lua_gettop(L)-1, LUA_MULTRET); - return lua_gettop(L); -} - /*-------------------------------------------------------------------------*\ * Return userdata pointer if object belongs to a given class, abort with * error otherwise diff --git a/src/auxiliar.h b/src/auxiliar.h index 324e800..b98eb9c 100644 --- a/src/auxiliar.h +++ b/src/auxiliar.h @@ -48,7 +48,6 @@ void *aux_checkclass(lua_State *L, const char *classname, int objidx); void *aux_checkgroup(lua_State *L, const char *groupname, int objidx); void *aux_getclassudata(lua_State *L, const char *groupname, int objidx); void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx); -int aux_meth_setoption(lua_State *L, luaL_reg *opt); int aux_checkboolean(lua_State *L, int objidx); #endif /* AUX_H */ diff --git a/src/inet.c b/src/inet.c index 6aea596..f6c2a6f 100644 --- a/src/inet.c +++ b/src/inet.c @@ -177,14 +177,22 @@ static void inet_pushresolved(lua_State *L, struct hostent *hp) lua_settable(L, resolved); } +/*-------------------------------------------------------------------------*\ +* Tries to create a new inet socket +\*-------------------------------------------------------------------------*/ +const char *inet_trycreate(p_sock ps, int type) +{ + return sock_create(ps, AF_INET, type, 0); +} + /*-------------------------------------------------------------------------*\ * Tries to connect to remote address (address, port) \*-------------------------------------------------------------------------*/ -const char *inet_tryconnect(p_sock ps, p_tm tm, const char *address, - unsigned short port) +const char *inet_tryconnect(p_sock ps, const char *address, + unsigned short port, p_tm tm) { struct sockaddr_in remote; - int err; + const char *err; memset(&remote, 0, sizeof(remote)); remote.sin_family = AF_INET; remote.sin_port = htons(port); @@ -197,14 +205,9 @@ const char *inet_tryconnect(p_sock ps, p_tm tm, const char *address, memcpy(&remote.sin_addr, *addr, sizeof(struct in_addr)); } } else remote.sin_family = AF_UNSPEC; - do err = sock_connect(ps, (SA *) &remote, sizeof(remote), tm_getretry(tm)); - while (err == IO_RETRY && tm_getretry(tm)); - if (err != IO_DONE) { - sock_destroy(ps); - *ps = SOCK_INVALID; - if (err == IO_ERROR) return sock_connectstrerror(); - else return io_strerror(err); - } else return NULL; + err = sock_connect(ps, (SA *) &remote, sizeof(remote), tm); + if (err) sock_destroy(ps); + return err; } /*-------------------------------------------------------------------------*\ @@ -214,6 +217,7 @@ const char *inet_trybind(p_sock ps, const char *address, unsigned short port, int backlog) { struct sockaddr_in local; + const char *err; memset(&local, 0, sizeof(local)); /* address is either wildcard or a valid ip address */ local.sin_addr.s_addr = htonl(INADDR_ANY); @@ -228,10 +232,10 @@ const char *inet_trybind(p_sock ps, const char *address, unsigned short port, memcpy(&local.sin_addr, *addr, sizeof(struct in_addr)); } sock_setblocking(ps); - if (sock_bind(ps, (SA *) &local, sizeof(local)) != IO_DONE) { + err = sock_bind(ps, (SA *) &local, sizeof(local)); + if (err) { sock_destroy(ps); - *ps = SOCK_INVALID; - return sock_bindstrerror(); + return err; } else { sock_setnonblocking(ps); if (backlog >= 0) sock_listen(ps, backlog); @@ -239,30 +243,6 @@ const char *inet_trybind(p_sock ps, const char *address, unsigned short port, } } -/*-------------------------------------------------------------------------*\ -* Tries to create a new inet socket -\*-------------------------------------------------------------------------*/ -const char *inet_trycreate(p_sock ps, int type) -{ - if (sock_create(ps, AF_INET, type, 0) == IO_DONE) return NULL; - else return sock_createstrerror(); -} - -/*-------------------------------------------------------------------------*\ -* Tries to accept an inet socket -\*-------------------------------------------------------------------------*/ -const char *inet_tryaccept(p_sock ps, p_tm tm, p_sock pc) -{ - struct sockaddr_in addr; - socklen_t addr_len = sizeof(addr); - int err; - /* loop until connection accepted or timeout happens */ - do err = sock_accept(ps, pc, (SA *) &addr, &addr_len, tm_getretry(tm)); - while (err == IO_RETRY && tm_getretry(tm) != 0); - if (err == IO_RETRY) err = IO_TIMEOUT; - return io_strerror(err); -} - /*-------------------------------------------------------------------------*\ * Some systems do not provide this so that we provide our own. It's not * marvelously fast, but it works just fine. diff --git a/src/inet.h b/src/inet.h index ad52801..28cf823 100644 --- a/src/inet.h +++ b/src/inet.h @@ -26,12 +26,11 @@ void inet_open(lua_State *L); -const char *inet_tryconnect(p_sock ps, p_tm tm, const char *address, - unsigned short port); +const char *inet_trycreate(p_sock ps, int type); +const char *inet_tryconnect(p_sock ps, const char *address, + unsigned short port, p_tm tm); const char *inet_trybind(p_sock ps, const char *address, unsigned short port, int backlog); -const char *inet_trycreate(p_sock ps, int type); -const char *inet_tryaccept(p_sock ps, p_tm tm, p_sock pc); int inet_meth_getpeername(lua_State *L, p_sock ps); int inet_meth_getsockname(lua_State *L, p_sock ps); diff --git a/src/options.c b/src/options.c new file mode 100644 index 0000000..32a98d6 --- /dev/null +++ b/src/options.c @@ -0,0 +1,137 @@ +#include +#include + +#include "auxiliar.h" +#include "options.h" + +static int opt_setmembership(lua_State *L, p_sock ps, int level, int name); +static int opt_setboolean(lua_State *L, p_sock ps, int level, int name); +static int opt_set(lua_State *L, p_sock ps, int level, int name, + void *val, int len); + +/*=========================================================================*\ +* Exported functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Calls appropriate option handler +\*-------------------------------------------------------------------------*/ +int opt_meth_setoption(lua_State *L, p_opt opt, p_sock ps) +{ + const char *name = luaL_checkstring(L, 2); /* obj, name, ... */ + while (opt->name && strcmp(name, opt->name)) + opt++; + if (!opt->func) { + char msg[45]; + sprintf(msg, "unsupported option `%.35s'", name); + luaL_argerror(L, 2, msg); + } + return opt->func(L, ps); +} + +/* enables reuse of local address */ +int opt_reuseaddr(lua_State *L, p_sock ps) +{ + return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEADDR); +} + +/* disables the Naggle algorithm */ +int opt_tcp_nodelay(lua_State *L, p_sock ps) +{ + return opt_setboolean(L, ps, IPPROTO_TCP, TCP_NODELAY); +} + +int opt_keepalive(lua_State *L, p_sock ps) +{ + return opt_setboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE); +} + +int opt_dontroute(lua_State *L, p_sock ps) +{ + return opt_setboolean(L, ps, SOL_SOCKET, SO_DONTROUTE); +} + +int opt_broadcast(lua_State *L, p_sock ps) +{ + return opt_setboolean(L, ps, SOL_SOCKET, SO_BROADCAST); +} + +int opt_ip_multicast_loop(lua_State *L, p_sock ps) +{ + return opt_setboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP); +} + +int opt_linger(lua_State *L, p_sock ps) +{ + struct linger li; /* obj, name, table */ + if (!lua_istable(L, 3)) luaL_typerror(L, 3, lua_typename(L, LUA_TTABLE)); + lua_pushstring(L, "on"); + lua_gettable(L, 3); + if (!lua_isboolean(L, -1)) + luaL_argerror(L, 3, "boolean 'on' field expected"); + li.l_onoff = lua_toboolean(L, -1); + lua_pushstring(L, "timeout"); + lua_gettable(L, 3); + if (!lua_isnumber(L, -1)) + luaL_argerror(L, 3, "number 'timeout' field expected"); + li.l_linger = (int) lua_tonumber(L, -1); + return opt_set(L, ps, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(li)); +} + +int opt_ip_multicast_ttl(lua_State *L, p_sock ps) +{ + int val = (int) luaL_checknumber(L, 3); /* obj, name, int */ + return opt_set(L, ps, SOL_SOCKET, SO_LINGER, (char *) &val, sizeof(val)); +} + +int opt_ip_add_membership(lua_State *L, p_sock ps) +{ + return opt_setmembership(L, ps, IPPROTO_IP, IP_ADD_MEMBERSHIP); +} + +int opt_ip_drop_membersip(lua_State *L, p_sock ps) +{ + return opt_setmembership(L, ps, IPPROTO_IP, IP_DROP_MEMBERSHIP); +} + +/*=========================================================================*\ +* Auxiliar functions +\*=========================================================================*/ +static int opt_setmembership(lua_State *L, p_sock ps, int level, int name) +{ + struct ip_mreq val; /* obj, name, table */ + if (!lua_istable(L, 3)) luaL_typerror(L, 3, lua_typename(L, LUA_TTABLE)); + lua_pushstring(L, "multiaddr"); + lua_gettable(L, 3); + if (!lua_isstring(L, -1)) + luaL_argerror(L, 3, "string 'multiaddr' field expected"); + if (!inet_aton(lua_tostring(L, -1), &val.imr_multiaddr)) + luaL_argerror(L, 3, "invalid 'multiaddr' ip address"); + lua_pushstring(L, "interface"); + lua_gettable(L, 3); + if (!lua_isstring(L, -1)) + luaL_argerror(L, 3, "string 'interface' field expected"); + val.imr_interface.s_addr = htonl(INADDR_ANY); + if (strcmp(lua_tostring(L, -1), "*") && + !inet_aton(lua_tostring(L, -1), &val.imr_interface)) + luaL_argerror(L, 3, "invalid 'interface' ip address"); + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); +} + +static +int opt_set(lua_State *L, p_sock ps, int level, int name, void *val, int len) +{ + if (setsockopt(*ps, level, name, (char *) val, len) < 0) { + lua_pushnil(L); + lua_pushstring(L, "setsockopt failed"); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +static int opt_setboolean(lua_State *L, p_sock ps, int level, int name) +{ + int val = aux_checkboolean(L, 3); /* obj, name, bool */ + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); +} + diff --git a/src/options.h b/src/options.h new file mode 100644 index 0000000..ec75fc0 --- /dev/null +++ b/src/options.h @@ -0,0 +1,30 @@ +#ifndef OPTIONS_H +#define OPTIONS_H + +#include +#include "socket.h" + +/* option registry */ +typedef struct t_opt { + const char *name; + int (*func)(lua_State *L, p_sock ps); +} t_opt; +typedef t_opt *p_opt; + +/* supported options */ +int opt_dontroute(lua_State *L, p_sock ps); +int opt_broadcast(lua_State *L, p_sock ps); +int opt_reuseaddr(lua_State *L, p_sock ps); +int opt_tcp_nodelay(lua_State *L, p_sock ps); +int opt_keepalive(lua_State *L, p_sock ps); +int opt_linger(lua_State *L, p_sock ps); +int opt_reuseaddr(lua_State *L, p_sock ps); +int opt_ip_multicast_ttl(lua_State *L, p_sock ps); +int opt_ip_multicast_loop(lua_State *L, p_sock ps); +int opt_ip_add_membership(lua_State *L, p_sock ps); +int opt_ip_drop_membersip(lua_State *L, p_sock ps); + +/* invokes the appropriate option handler */ +int opt_meth_setoption(lua_State *L, p_opt opt, p_sock ps); + +#endif diff --git a/src/socket.h b/src/socket.h index 739dedd..7c84baa 100644 --- a/src/socket.h +++ b/src/socket.h @@ -22,6 +22,14 @@ #include "usocket.h" #endif +/*=========================================================================*\ +* The connect and accept functions accept a timeout and their +* implementations are somewhat complicated. We chose to move +* the timeout control into this module for these functions in +* order to simplify the modules that use them. +\*=========================================================================*/ +#include "timeout.h" + /* we are lazy... */ typedef struct sockaddr SA; @@ -30,12 +38,7 @@ typedef struct sockaddr SA; * interface to sockets \*=========================================================================*/ int sock_open(void); -int sock_create(p_sock ps, int domain, int type, int protocol); void sock_destroy(p_sock ps); -int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len, - int timeout); -int sock_connect(p_sock ps, SA *addr, socklen_t addr_len, int timeout); -int sock_bind(p_sock ps, SA *addr, socklen_t addr_len); void sock_listen(p_sock ps, int backlog); void sock_shutdown(p_sock ps, int how); int sock_send(p_sock ps, const char *data, size_t count, @@ -48,10 +51,10 @@ int sock_recvfrom(p_sock ps, char *data, size_t count, size_t *got, SA *addr, socklen_t *addr_len, int timeout); void sock_setnonblocking(p_sock ps); void sock_setblocking(p_sock ps); - +int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len, p_tm tm); +const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len, p_tm tm); +const char *sock_create(p_sock ps, int domain, int type, int protocol); +const char *sock_bind(p_sock ps, SA *addr, socklen_t addr_len); const char *sock_hoststrerror(void); -const char *sock_createstrerror(void); -const char *sock_bindstrerror(void); -const char *sock_connectstrerror(void); #endif /* SOCK_H */ diff --git a/src/tcp.c b/src/tcp.c index 5505848..01b07ee 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -14,6 +14,7 @@ #include "auxiliar.h" #include "socket.h" #include "inet.h" +#include "options.h" #include "tcp.h" /*=========================================================================*\ @@ -33,10 +34,6 @@ static int meth_setoption(lua_State *L); static int meth_settimeout(lua_State *L); static int meth_fd(lua_State *L); static int meth_dirty(lua_State *L); -static int opt_tcp_nodelay(lua_State *L); -static int opt_keepalive(lua_State *L); -static int opt_linger(lua_State *L); -static int opt_reuseaddr(lua_State *L); /* tcp object methods */ static luaL_reg tcp[] = { @@ -60,7 +57,7 @@ static luaL_reg tcp[] = { }; /* socket option handlers */ -static luaL_reg opt[] = { +static t_opt opt[] = { {"keepalive", opt_keepalive}, {"reuseaddr", opt_reuseaddr}, {"tcp-nodelay", opt_tcp_nodelay}, @@ -116,65 +113,12 @@ static int meth_receive(lua_State *L) } /*-------------------------------------------------------------------------*\ -* Option handlers +* Just call option handler \*-------------------------------------------------------------------------*/ static int meth_setoption(lua_State *L) { - return aux_meth_setoption(L, opt); -} - -static int opt_boolean(lua_State *L, int level, int name) -{ - p_tcp tcp = (p_tcp) aux_checkgroup(L, "tcp{any}", 1); - int val = aux_checkboolean(L, 2); - if (setsockopt(tcp->sock, level, name, (char *) &val, sizeof(val)) < 0) { - lua_pushnil(L); - lua_pushstring(L, "setsockopt failed"); - return 2; - } - lua_pushnumber(L, 1); - return 1; -} - -/* enables reuse of local address */ -static int opt_reuseaddr(lua_State *L) -{ - return opt_boolean(L, SOL_SOCKET, SO_REUSEADDR); -} - -/* disables the Naggle algorithm */ -static int opt_tcp_nodelay(lua_State *L) -{ - return opt_boolean(L, IPPROTO_TCP, TCP_NODELAY); -} - -static int opt_keepalive(lua_State *L) -{ - return opt_boolean(L, SOL_SOCKET, SO_KEEPALIVE); -} - -static int opt_linger(lua_State *L) -{ - p_tcp tcp = (p_tcp) aux_checkclass(L, "tcp{client}", 1); - struct linger li; - if (!lua_istable(L, 2)) - luaL_typerror(L, 2, lua_typename(L, LUA_TTABLE)); - lua_pushstring(L, "on"); - lua_gettable(L, 2); - if (!lua_isboolean(L, -1)) luaL_argerror(L, 2, "invalid 'on' field"); - li.l_onoff = lua_toboolean(L, -1); - lua_pushstring(L, "timeout"); - lua_gettable(L, 2); - if (!lua_isnumber(L, -1)) luaL_argerror(L, 2, "invalid 'timeout' field"); - li.l_linger = (int) lua_tonumber(L, -1); - if (setsockopt(tcp->sock, SOL_SOCKET, SO_LINGER, - (char *) &li, sizeof(li) < 0)) { - lua_pushnil(L); - lua_pushstring(L, "setsockopt failed"); - return 2; - } - lua_pushnumber(L, 1); - return 1; + p_tcp tcp = aux_checkgroup(L, "tcp{any}", 1); + return opt_meth_setoption(L, opt, &tcp->sock); } /*-------------------------------------------------------------------------*\ @@ -201,13 +145,11 @@ static int meth_dirty(lua_State *L) static int meth_accept(lua_State *L) { p_tcp server = (p_tcp) aux_checkclass(L, "tcp{server}", 1); - p_tm tm = &server->tm; + p_tm tm = tm_markstart(&server->tm); t_sock sock; - const char *err; - tm_markstart(tm); - err = inet_tryaccept(&server->sock, tm, &sock); + int err = sock_accept(&server->sock, &sock, NULL, NULL, tm); /* if successful, push client socket */ - if (!err) { + if (err == IO_DONE) { p_tcp clnt = lua_newuserdata(L, sizeof(t_tcp)); aux_setclass(L, "tcp{client}", -1); /* initialize structure fields */ @@ -218,7 +160,7 @@ static int meth_accept(lua_State *L) return 1; } else { lua_pushnil(L); - lua_pushstring(L, err); + io_pusherror(L, err); return 2; } } @@ -252,10 +194,8 @@ static int meth_connect(lua_State *L) p_tcp tcp = (p_tcp) aux_checkclass(L, "tcp{master}", 1); const char *address = luaL_checkstring(L, 2); unsigned short port = (unsigned short) luaL_checknumber(L, 3); - p_tm tm = &tcp->tm; - const char *err; - tm_markstart(tm); - err = inet_tryconnect(&tcp->sock, tm, address, port); + p_tm tm = tm_markstart(&tcp->tm); + const char *err = inet_tryconnect(&tcp->sock, address, port, tm); if (err) { lua_pushnil(L); lua_pushstring(L, err); diff --git a/src/tcp.h b/src/tcp.h index 82b88a9..ae99b8c 100644 --- a/src/tcp.h +++ b/src/tcp.h @@ -28,6 +28,7 @@ typedef struct t_tcp_ { t_buf buf; t_tm tm; } t_tcp; + typedef t_tcp *p_tcp; void tcp_open(lua_State *L); diff --git a/src/timeout.c b/src/timeout.c index 09cb53d..83bffc9 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -114,9 +114,10 @@ int tm_getretry(p_tm tm) * Input * tm: timeout control structure \*-------------------------------------------------------------------------*/ -void tm_markstart(p_tm tm) +p_tm tm_markstart(p_tm tm) { tm->start = tm_gettime(); + return tm; } /*-------------------------------------------------------------------------*\ diff --git a/src/timeout.h b/src/timeout.h index 0a036f4..6584a8e 100644 --- a/src/timeout.h +++ b/src/timeout.h @@ -20,7 +20,7 @@ void tm_open(lua_State *L); void tm_init(p_tm tm, int block, int total); int tm_get(p_tm tm); int tm_getretry(p_tm tm); -void tm_markstart(p_tm tm); +p_tm tm_markstart(p_tm tm); int tm_getstart(p_tm tm); int tm_gettime(void); int tm_meth_settimeout(lua_State *L, p_tm tm); diff --git a/src/udp.c b/src/udp.c index c5e14d1..115e749 100644 --- a/src/udp.c +++ b/src/udp.c @@ -14,6 +14,7 @@ #include "auxiliar.h" #include "socket.h" #include "inet.h" +#include "options.h" #include "udp.h" /*=========================================================================*\ @@ -34,13 +35,6 @@ static int meth_setoption(lua_State *L); static int meth_settimeout(lua_State *L); static int meth_fd(lua_State *L); static int meth_dirty(lua_State *L); -static int opt_dontroute(lua_State *L); -static int opt_broadcast(lua_State *L); -static int opt_reuseaddr(lua_State *L); -static int opt_ip_multicast_ttl(lua_State *L); -static int opt_ip_multicast_loop(lua_State *L); -static int opt_ip_add_membership(lua_State *L); -static int opt_ip_drop_membersip(lua_State *L); /* udp object methods */ static luaL_reg udp[] = { @@ -63,7 +57,7 @@ static luaL_reg udp[] = { }; /* socket options */ -static luaL_reg opt[] = { +static t_opt opt[] = { {"dontroute", opt_dontroute}, {"broadcast", opt_broadcast}, {"reuseaddr", opt_reuseaddr}, @@ -231,95 +225,12 @@ static int meth_getsockname(lua_State *L) } /*-------------------------------------------------------------------------*\ -* Option handlers +* Just call option handler \*-------------------------------------------------------------------------*/ static int meth_setoption(lua_State *L) -{ - return aux_meth_setoption(L, opt); -} - -static int opt_boolean(lua_State *L, int level, int name) { p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1); - int val = aux_checkboolean(L, 2); - if (setsockopt(udp->sock, level, name, (char *) &val, sizeof(val)) < 0) { - lua_pushnil(L); - lua_pushstring(L, "setsockopt failed"); - return 2; - } - lua_pushnumber(L, 1); - return 1; -} - -static int opt_dontroute(lua_State *L) -{ - return opt_boolean(L, SOL_SOCKET, SO_DONTROUTE); -} - -static int opt_reuseaddr(lua_State *L) -{ - return opt_boolean(L, SOL_SOCKET, SO_REUSEADDR); -} - -static int opt_broadcast(lua_State *L) -{ - return opt_boolean(L, SOL_SOCKET, SO_BROADCAST); -} - -static int opt_ip_multicast_loop(lua_State *L) -{ - return opt_boolean(L, IPPROTO_IP, IP_MULTICAST_LOOP); -} - -static int opt_ip_multicast_ttl(lua_State *L) -{ - p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1); - int val = (int) luaL_checknumber(L, 2); - if (setsockopt(udp->sock, IPPROTO_IP, IP_MULTICAST_TTL, - (char *) &val, sizeof(val)) < 0) { - lua_pushnil(L); - lua_pushstring(L, "setsockopt failed"); - return 2; - } - lua_pushnumber(L, 1); - return 1; -} - -static int opt_membership(lua_State *L, int level, int name) -{ - p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1); - struct ip_mreq val; - if (!lua_istable(L, 2)) - luaL_typerror(L, 2, lua_typename(L, LUA_TTABLE)); - lua_pushstring(L, "multiaddr"); - lua_gettable(L, 2); - if (!lua_isstring(L, -1)) luaL_argerror(L, 2, "invalid 'group' field"); - if (!inet_aton(lua_tostring(L, -1), &val.imr_multiaddr)) - luaL_argerror(L, 3, "invalid 'multiaddr' ip address"); - lua_pushstring(L, "interface"); - lua_gettable(L, 2); - if (!lua_isstring(L, -1)) luaL_argerror(L, 2, "invalid 'interface' field"); - val.imr_interface.s_addr = htonl(INADDR_ANY); - if (strcmp(lua_tostring(L, -1), "*") && - !inet_aton(lua_tostring(L, -1), &val.imr_interface)) - luaL_argerror(L, 3, "invalid 'interface' ip address"); - if (setsockopt(udp->sock, level, name, (char *) &val, sizeof(val)) < 0) { - lua_pushnil(L); - lua_pushstring(L, "setsockopt failed"); - return 2; - } - lua_pushnumber(L, 1); - return 1; -} - -static int opt_ip_add_membership(lua_State *L) -{ - return opt_membership(L, IPPROTO_IP, IP_ADD_MEMBERSHIP); -} - -static int opt_ip_drop_membersip(lua_State *L) -{ - return opt_membership(L, IPPROTO_IP, IP_DROP_MEMBERSHIP); + return opt_meth_setoption(L, opt, &udp->sock); } /*-------------------------------------------------------------------------*\ @@ -343,7 +254,7 @@ static int meth_setpeername(lua_State *L) unsigned short port = connecting ? (unsigned short) luaL_checknumber(L, 3) : (unsigned short) luaL_optnumber(L, 3, 0); - const char *err = inet_tryconnect(&udp->sock, tm, address, port); + const char *err = inet_tryconnect(&udp->sock, address, port, tm); if (err) { lua_pushnil(L); lua_pushstring(L, err); diff --git a/src/usocket.c b/src/usocket.c index f5939a9..54f203b 100644 --- a/src/usocket.c +++ b/src/usocket.c @@ -20,6 +20,10 @@ #include "socket.h" +static const char *sock_createstrerror(void); +static const char *sock_bindstrerror(void); +static const char *sock_connectstrerror(void); + /*-------------------------------------------------------------------------*\ * Initializes module \*-------------------------------------------------------------------------*/ @@ -50,31 +54,37 @@ void sock_destroy(p_sock ps) /*-------------------------------------------------------------------------*\ * Creates and sets up a socket \*-------------------------------------------------------------------------*/ -int sock_create(p_sock ps, int domain, int type, int protocol) +const char *sock_create(p_sock ps, int domain, int type, int protocol) { int val = 1; t_sock sock = socket(domain, type, protocol); - if (sock == SOCK_INVALID) return IO_ERROR; + if (sock == SOCK_INVALID) return sock_createstrerror(); *ps = sock; sock_setnonblocking(ps); setsockopt(*ps, SOL_SOCKET, SO_REUSEADDR, (char *) &val, sizeof(val)); - return IO_DONE; + return NULL; } /*-------------------------------------------------------------------------*\ * Connects or returns error message \*-------------------------------------------------------------------------*/ -int sock_connect(p_sock ps, SA *addr, socklen_t addr_len, int timeout) +const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len, p_tm tm) { t_sock sock = *ps; - if (sock == SOCK_INVALID) return IO_CLOSED; - /* if connect fails, we have to find out why */ - if (connect(sock, addr, addr_len) < 0) { + int err; + /* don't call on closed socket */ + if (sock == SOCK_INVALID) return io_strerror(IO_CLOSED); + /* ask system to connect */ + err = connect(sock, addr, addr_len); + /* if no error, we're done */ + if (err == 0) return NULL; + /* make sure the system is trying to connect */ + if (errno != EINPROGRESS) return io_strerror(IO_ERROR); + /* wait for a timeout or for the system's answer */ + for ( ;; ) { struct timeval tv; - fd_set rfds, efds, wfds; - int err; - /* make sure the system is trying to connect */ - if (errno != EINPROGRESS) return IO_ERROR; + fd_set rfds, wfds, efds; + int timeout = tm_getretry(tm); tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; FD_ZERO(&rfds); FD_SET(sock, &rfds); @@ -82,28 +92,29 @@ int sock_connect(p_sock ps, SA *addr, socklen_t addr_len, int timeout) FD_ZERO(&efds); FD_SET(sock, &efds); /* we run select to avoid busy waiting */ err = select(sock+1, &rfds, &wfds, &efds, timeout >= 0? &tv: NULL); - /* if select was interrupted, ask the user to retry */ - if (err < 0 && errno == EINTR) return IO_RETRY; + /* if select was interrupted, try again */ + if (err < 0 && errno == EINTR) continue; /* if selects readable, try reading */ if (err > 0) { char dummy; - /* try reading so that errno is set */ + /* recv will set errno to the value a blocking connect would set */ if (recv(sock, &dummy, 0, 0) < 0 && errno != EAGAIN) - return IO_ERROR; - else return IO_DONE; + return sock_connectstrerror(); + else + return NULL; /* if no event happened, there was a timeout */ - } else return IO_TIMEOUT; - /* otherwise connection succeeded */ - } else return IO_DONE; + } else return io_strerror(IO_TIMEOUT); + } + return io_strerror(IO_TIMEOUT); /* can't get here */ } /*-------------------------------------------------------------------------*\ * Binds or returns error message \*-------------------------------------------------------------------------*/ -int sock_bind(p_sock ps, SA *addr, socklen_t addr_len) +const char *sock_bind(p_sock ps, SA *addr, socklen_t addr_len) { - if (bind(*ps, addr, addr_len) < 0) return IO_ERROR; - else return IO_DONE; + if (bind(*ps, addr, addr_len) < 0) return sock_bindstrerror(); + else return NULL; } /*-------------------------------------------------------------------------*\ @@ -125,8 +136,7 @@ void sock_shutdown(p_sock ps, int how) /*-------------------------------------------------------------------------*\ * Accept with timeout \*-------------------------------------------------------------------------*/ -int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len, - int timeout) +int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len, p_tm tm) { t_sock sock = *ps; SA dummy_addr; @@ -134,19 +144,21 @@ int sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len, if (sock == SOCK_INVALID) return IO_CLOSED; if (!addr) addr = &dummy_addr; if (!addr_len) addr_len = &dummy_len; - *pa = accept(sock, addr, addr_len); - if (*pa == SOCK_INVALID) { + for (;;) { + int timeout = tm_getretry(tm); struct timeval tv; fd_set fds; + *pa = accept(sock, addr, addr_len); + if (*pa != SOCK_INVALID) return IO_DONE; + if (timeout == 0) return IO_TIMEOUT; tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; FD_ZERO(&fds); FD_SET(sock, &fds); - /* just call select to avoid busy-wait. doesn't really matter - * what happens. the caller will choose to retry or not */ + /* call select just to avoid busy-wait. */ select(sock+1, &fds, NULL, NULL, timeout >= 0? &tv: NULL); - return IO_RETRY; - } else return IO_DONE; + } + return IO_TIMEOUT; /* can't get here */ } /*-------------------------------------------------------------------------*\ @@ -314,7 +326,7 @@ const char *sock_hoststrerror(void) } } -const char *sock_createstrerror(void) +static const char *sock_createstrerror(void) { switch (errno) { case EACCES: return "access denied"; @@ -325,7 +337,7 @@ const char *sock_createstrerror(void) } } -const char *sock_bindstrerror(void) +static const char *sock_bindstrerror(void) { switch (errno) { case EBADF: return "invalid descriptor"; @@ -339,7 +351,7 @@ const char *sock_bindstrerror(void) } } -const char *sock_connectstrerror(void) +static const char *sock_connectstrerror(void) { switch (errno) { case EBADF: return "invalid descriptor"; diff --git a/test/httptest.lua b/test/httptest.lua index 4667605..8bf3980 100644 --- a/test/httptest.lua +++ b/test/httptest.lua @@ -49,8 +49,7 @@ local check_result = function(response, expect, ignore) for i,v in response do if not ignore[i] then if v ~= expect[i] then - v = string.sub(type(v) == "string" and v or "", 1, 70) - print(v) + print(string.sub(tostring(v), 1, 70)) fail(i .. " differs!") end end @@ -59,7 +58,7 @@ local check_result = function(response, expect, ignore) if not ignore[i] then if v ~= response[i] then v = string.sub(type(v) == "string" and v or "", 1, 70) - print(v) + print(string.sub(tostring(v), 1, 70)) fail(i .. " differs!") end end diff --git a/test/testclnt.lua b/test/testclnt.lua index 5a001b3..a0d9fda 100644 --- a/test/testclnt.lua +++ b/test/testclnt.lua @@ -378,6 +378,19 @@ function connect_timeout() c:close() end +------------------------------------------------------------------------ +function rebind_test() + local c = socket.bind("localhost", 0) + local i, p = c:getsockname() + local s, e = socket.tcp() + assert(s, e) + s:setoption("reuseaddr", false) + r, e = s:bind("localhost", p) + assert(not r, "managed to rebind!") + assert(e == "address already in use") + print("ok") +end + ------------------------------------------------------------------------ test("method registration") test_methods(socket.tcp(), { @@ -416,6 +429,9 @@ test_selectbugs() test("empty host connect: ") empty_connect() +test("rebinding: ") +rebind_test() + test("active close: ") active_close()