Fixed functions that return messages in ?socket.c.

Moved complexity of connect and accept there.
Created a new options.c module to take care of options.
Auxiliar.c is now cleaner.
This commit is contained in:
Diego Nehab 2004-01-21 18:40:52 +00:00
parent e63f500d24
commit 195069cf5f
16 changed files with 284 additions and 275 deletions

1
TODO
View File

@ -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!

View File

@ -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

View File

@ -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 */

View File

@ -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.

View File

@ -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);

137
src/options.c Normal file
View File

@ -0,0 +1,137 @@
#include <lauxlib.h>
#include <string.h>
#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));
}

30
src/options.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef OPTIONS_H
#define OPTIONS_H
#include <lua.h>
#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

View File

@ -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 */

View File

@ -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);

View File

@ -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);

View File

@ -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;
}
/*-------------------------------------------------------------------------*\

View File

@ -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);

View File

@ -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);

View File

@ -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";

View File

@ -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

View File

@ -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()