diff --git a/src/makefile b/src/makefile index 2e00950..2ffd7d9 100644 --- a/src/makefile +++ b/src/makefile @@ -307,6 +307,9 @@ UNIX_OBJS=\ timeout.$(O) \ io.$(O) \ usocket.$(O) \ + unixtcp.$(O) \ + unixudp.$(O) \ + compat.$(O) \ unix.$(O) #------ diff --git a/src/unix.c b/src/unix.c index 5bc3148..2009c19 100644 --- a/src/unix.c +++ b/src/unix.c @@ -2,329 +2,29 @@ * Unix domain socket * LuaSocket toolkit \*=========================================================================*/ -#include - #include "lua.h" #include "lauxlib.h" -#include "auxiliar.h" -#include "socket.h" -#include "options.h" -#include "unix.h" -#include +#include "unixtcp.h" +#include "unixudp.h" -/*=========================================================================*\ -* Internal function prototypes -\*=========================================================================*/ -static int global_create(lua_State *L); -static int meth_connect(lua_State *L); -static int meth_listen(lua_State *L); -static int meth_bind(lua_State *L); -static int meth_send(lua_State *L); -static int meth_shutdown(lua_State *L); -static int meth_receive(lua_State *L); -static int meth_accept(lua_State *L); -static int meth_close(lua_State *L); -static int meth_setoption(lua_State *L); -static int meth_settimeout(lua_State *L); -static int meth_getfd(lua_State *L); -static int meth_setfd(lua_State *L); -static int meth_dirty(lua_State *L); -static int meth_getstats(lua_State *L); -static int meth_setstats(lua_State *L); - -static const char *unix_tryconnect(p_unix un, const char *path); -static const char *unix_trybind(p_unix un, const char *path); - -/* unix object methods */ -static luaL_Reg unix_methods[] = { - {"__gc", meth_close}, - {"__tostring", auxiliar_tostring}, - {"accept", meth_accept}, - {"bind", meth_bind}, - {"close", meth_close}, - {"connect", meth_connect}, - {"dirty", meth_dirty}, - {"getfd", meth_getfd}, - {"getstats", meth_getstats}, - {"setstats", meth_setstats}, - {"listen", meth_listen}, - {"receive", meth_receive}, - {"send", meth_send}, - {"setfd", meth_setfd}, - {"setoption", meth_setoption}, - {"setpeername", meth_connect}, - {"setsockname", meth_bind}, - {"settimeout", meth_settimeout}, - {"shutdown", meth_shutdown}, - {NULL, NULL} -}; - -/* socket option handlers */ -static t_opt optset[] = { - {"keepalive", opt_set_keepalive}, - {"reuseaddr", opt_set_reuseaddr}, - {"linger", opt_set_linger}, - {NULL, NULL} +/*-------------------------------------------------------------------------*\ +* Modules and functions +\*-------------------------------------------------------------------------*/ +static const luaL_Reg mod[] = { + {"tcp", unixtcp_open}, + {"udp", unixudp_open}, + {NULL, NULL} }; /*-------------------------------------------------------------------------*\ * Initializes module \*-------------------------------------------------------------------------*/ -int luaopen_socket_unix(lua_State *L) { - /* create classes */ - auxiliar_newclass(L, "unix{master}", unix_methods); - auxiliar_newclass(L, "unix{client}", unix_methods); - auxiliar_newclass(L, "unix{server}", unix_methods); - /* create class groups */ - auxiliar_add2group(L, "unix{master}", "unix{any}"); - auxiliar_add2group(L, "unix{client}", "unix{any}"); - auxiliar_add2group(L, "unix{server}", "unix{any}"); - /* return the function instead of the 'socket' table */ - lua_pushcfunction(L, global_create); - return 1; -} - -/*=========================================================================*\ -* Lua methods -\*=========================================================================*/ -/*-------------------------------------------------------------------------*\ -* Just call buffered IO methods -\*-------------------------------------------------------------------------*/ -static int meth_send(lua_State *L) { - p_unix un = (p_unix) auxiliar_checkclass(L, "unix{client}", 1); - return buffer_meth_send(L, &un->buf); -} - -static int meth_receive(lua_State *L) { - p_unix un = (p_unix) auxiliar_checkclass(L, "unix{client}", 1); - return buffer_meth_receive(L, &un->buf); -} - -static int meth_getstats(lua_State *L) { - p_unix un = (p_unix) auxiliar_checkclass(L, "unix{client}", 1); - return buffer_meth_getstats(L, &un->buf); -} - -static int meth_setstats(lua_State *L) { - p_unix un = (p_unix) auxiliar_checkclass(L, "unix{client}", 1); - return buffer_meth_setstats(L, &un->buf); -} - -/*-------------------------------------------------------------------------*\ -* Just call option handler -\*-------------------------------------------------------------------------*/ -static int meth_setoption(lua_State *L) { - p_unix un = (p_unix) auxiliar_checkgroup(L, "unix{any}", 1); - return opt_meth_setoption(L, optset, &un->sock); -} - -/*-------------------------------------------------------------------------*\ -* Select support methods -\*-------------------------------------------------------------------------*/ -static int meth_getfd(lua_State *L) { - p_unix un = (p_unix) auxiliar_checkgroup(L, "unix{any}", 1); - lua_pushnumber(L, (int) un->sock); - return 1; -} - -/* this is very dangerous, but can be handy for those that are brave enough */ -static int meth_setfd(lua_State *L) { - p_unix un = (p_unix) auxiliar_checkgroup(L, "unix{any}", 1); - un->sock = (t_socket) luaL_checknumber(L, 2); - return 0; -} - -static int meth_dirty(lua_State *L) { - p_unix un = (p_unix) auxiliar_checkgroup(L, "unix{any}", 1); - lua_pushboolean(L, !buffer_isempty(&un->buf)); - return 1; -} - -/*-------------------------------------------------------------------------*\ -* Waits for and returns a client object attempting connection to the -* server object -\*-------------------------------------------------------------------------*/ -static int meth_accept(lua_State *L) { - p_unix server = (p_unix) auxiliar_checkclass(L, "unix{server}", 1); - p_timeout tm = timeout_markstart(&server->tm); - t_socket sock; - int err = socket_accept(&server->sock, &sock, NULL, NULL, tm); - /* if successful, push client socket */ - if (err == IO_DONE) { - p_unix clnt = (p_unix) lua_newuserdata(L, sizeof(t_unix)); - auxiliar_setclass(L, "unix{client}", -1); - /* initialize structure fields */ - socket_setnonblocking(&sock); - clnt->sock = sock; - io_init(&clnt->io, (p_send)socket_send, (p_recv)socket_recv, - (p_error) socket_ioerror, &clnt->sock); - timeout_init(&clnt->tm, -1, -1); - buffer_init(&clnt->buf, &clnt->io, &clnt->tm); - return 1; - } else { - lua_pushnil(L); - lua_pushstring(L, socket_strerror(err)); - return 2; - } -} - -/*-------------------------------------------------------------------------*\ -* Binds an object to an address -\*-------------------------------------------------------------------------*/ -static const char *unix_trybind(p_unix un, const char *path) { - struct sockaddr_un local; - size_t len = strlen(path); - int err; - if (len >= sizeof(local.sun_path)) return "path too long"; - memset(&local, 0, sizeof(local)); - strcpy(local.sun_path, path); - local.sun_family = AF_UNIX; -#ifdef UNIX_HAS_SUN_LEN - local.sun_len = sizeof(local.sun_family) + sizeof(local.sun_len) - + len + 1; - err = socket_bind(&un->sock, (SA *) &local, local.sun_len); - -#else - err = socket_bind(&un->sock, (SA *) &local, - sizeof(local.sun_family) + len); -#endif - if (err != IO_DONE) socket_destroy(&un->sock); - return socket_strerror(err); -} - -static int meth_bind(lua_State *L) { - p_unix un = (p_unix) auxiliar_checkclass(L, "unix{master}", 1); - const char *path = luaL_checkstring(L, 2); - const char *err = unix_trybind(un, path); - if (err) { - lua_pushnil(L); - lua_pushstring(L, err); - return 2; - } - lua_pushnumber(L, 1); - return 1; -} - -/*-------------------------------------------------------------------------*\ -* Turns a master unix object into a client object. -\*-------------------------------------------------------------------------*/ -static const char *unix_tryconnect(p_unix un, const char *path) +int luaopen_socket_unix(lua_State *L) { - struct sockaddr_un remote; - int err; - size_t len = strlen(path); - if (len >= sizeof(remote.sun_path)) return "path too long"; - memset(&remote, 0, sizeof(remote)); - strcpy(remote.sun_path, path); - remote.sun_family = AF_UNIX; - timeout_markstart(&un->tm); -#ifdef UNIX_HAS_SUN_LEN - remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len) - + len + 1; - err = socket_connect(&un->sock, (SA *) &remote, remote.sun_len, &un->tm); -#else - err = socket_connect(&un->sock, (SA *) &remote, - sizeof(remote.sun_family) + len, &un->tm); -#endif - if (err != IO_DONE) socket_destroy(&un->sock); - return socket_strerror(err); + int i; + lua_newtable(L); + for (i = 0; mod[i].name; i++) mod[i].func(L); + return 1; } -static int meth_connect(lua_State *L) -{ - p_unix un = (p_unix) auxiliar_checkclass(L, "unix{master}", 1); - const char *path = luaL_checkstring(L, 2); - const char *err = unix_tryconnect(un, path); - if (err) { - lua_pushnil(L); - lua_pushstring(L, err); - return 2; - } - /* turn master object into a client object */ - auxiliar_setclass(L, "unix{client}", 1); - lua_pushnumber(L, 1); - return 1; -} - -/*-------------------------------------------------------------------------*\ -* Closes socket used by object -\*-------------------------------------------------------------------------*/ -static int meth_close(lua_State *L) -{ - p_unix un = (p_unix) auxiliar_checkgroup(L, "unix{any}", 1); - socket_destroy(&un->sock); - lua_pushnumber(L, 1); - return 1; -} - -/*-------------------------------------------------------------------------*\ -* Puts the sockt in listen mode -\*-------------------------------------------------------------------------*/ -static int meth_listen(lua_State *L) -{ - p_unix un = (p_unix) auxiliar_checkclass(L, "unix{master}", 1); - int backlog = (int) luaL_optnumber(L, 2, 32); - int err = socket_listen(&un->sock, backlog); - if (err != IO_DONE) { - lua_pushnil(L); - lua_pushstring(L, socket_strerror(err)); - return 2; - } - /* turn master object into a server object */ - auxiliar_setclass(L, "unix{server}", 1); - lua_pushnumber(L, 1); - return 1; -} - -/*-------------------------------------------------------------------------*\ -* Shuts the connection down partially -\*-------------------------------------------------------------------------*/ -static int meth_shutdown(lua_State *L) -{ - /* SHUT_RD, SHUT_WR, SHUT_RDWR have the value 0, 1, 2, so we can use method index directly */ - static const char* methods[] = { "receive", "send", "both", NULL }; - p_unix tcp = (p_unix) auxiliar_checkclass(L, "unix{client}", 1); - int how = luaL_checkoption(L, 2, "both", methods); - socket_shutdown(&tcp->sock, how); - lua_pushnumber(L, 1); - return 1; -} - -/*-------------------------------------------------------------------------*\ -* Just call tm methods -\*-------------------------------------------------------------------------*/ -static int meth_settimeout(lua_State *L) { - p_unix un = (p_unix) auxiliar_checkgroup(L, "unix{any}", 1); - return timeout_meth_settimeout(L, &un->tm); -} - -/*=========================================================================*\ -* Library functions -\*=========================================================================*/ -/*-------------------------------------------------------------------------*\ -* Creates a master unix object -\*-------------------------------------------------------------------------*/ -static int global_create(lua_State *L) { - t_socket sock; - int err = socket_create(&sock, AF_UNIX, SOCK_STREAM, 0); - /* try to allocate a system socket */ - if (err == IO_DONE) { - /* allocate unix object */ - p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); - /* set its type as master object */ - auxiliar_setclass(L, "unix{master}", -1); - /* initialize remaining structure fields */ - socket_setnonblocking(&sock); - un->sock = sock; - io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv, - (p_error) socket_ioerror, &un->sock); - timeout_init(&un->tm, -1, -1); - buffer_init(&un->buf, &un->io, &un->tm); - return 1; - } else { - lua_pushnil(L); - lua_pushstring(L, socket_strerror(err)); - return 2; - } -} diff --git a/src/unixtcp.c b/src/unixtcp.c new file mode 100644 index 0000000..747ef5f --- /dev/null +++ b/src/unixtcp.c @@ -0,0 +1,357 @@ +/*=========================================================================*\ +* Unix domain socket tcp sub module +* LuaSocket toolkit +\*=========================================================================*/ +#include + +#include "lua.h" +#include "lauxlib.h" +#include "compat.h" + +#include "auxiliar.h" +#include "socket.h" +#include "options.h" +#include "unixtcp.h" +#include + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int global_create(lua_State *L); +static int meth_connect(lua_State *L); +static int meth_listen(lua_State *L); +static int meth_bind(lua_State *L); +static int meth_send(lua_State *L); +static int meth_shutdown(lua_State *L); +static int meth_receive(lua_State *L); +static int meth_accept(lua_State *L); +static int meth_close(lua_State *L); +static int meth_setoption(lua_State *L); +static int meth_settimeout(lua_State *L); +static int meth_getfd(lua_State *L); +static int meth_setfd(lua_State *L); +static int meth_dirty(lua_State *L); +static int meth_getstats(lua_State *L); +static int meth_setstats(lua_State *L); +static int meth_getsockname(lua_State *L); + +static const char *unixtcp_tryconnect(p_unix un, const char *path); +static const char *unixtcp_trybind(p_unix un, const char *path); + +/* unixtcp object methods */ +static luaL_Reg unixtcp_methods[] = { + {"__gc", meth_close}, + {"__tostring", auxiliar_tostring}, + {"accept", meth_accept}, + {"bind", meth_bind}, + {"close", meth_close}, + {"connect", meth_connect}, + {"dirty", meth_dirty}, + {"getfd", meth_getfd}, + {"getstats", meth_getstats}, + {"setstats", meth_setstats}, + {"listen", meth_listen}, + {"receive", meth_receive}, + {"send", meth_send}, + {"setfd", meth_setfd}, + {"setoption", meth_setoption}, + {"setpeername", meth_connect}, + {"setsockname", meth_bind}, + {"getsockname", meth_getsockname}, + {"settimeout", meth_settimeout}, + {"shutdown", meth_shutdown}, + {NULL, NULL} +}; + +/* socket option handlers */ +static t_opt optset[] = { + {"keepalive", opt_set_keepalive}, + {"reuseaddr", opt_set_reuseaddr}, + {"linger", opt_set_linger}, + {NULL, NULL} +}; + +/* functions in library namespace */ +static luaL_Reg func[] = { + {"tcp", global_create}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int unixtcp_open(lua_State *L) +{ + /* create classes */ + auxiliar_newclass(L, "unixtcp{master}", unixtcp_methods); + auxiliar_newclass(L, "unixtcp{client}", unixtcp_methods); + auxiliar_newclass(L, "unixtcp{server}", unixtcp_methods); + + /* create class groups */ + auxiliar_add2group(L, "unixtcp{master}", "unixtcp{any}"); + auxiliar_add2group(L, "unixtcp{client}", "unixtcp{any}"); + auxiliar_add2group(L, "unixtcp{server}", "unixtcp{any}"); + + luaL_setfuncs(L, func, 0); + return 0; +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Just call buffered IO methods +\*-------------------------------------------------------------------------*/ +static int meth_send(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixtcp{client}", 1); + return buffer_meth_send(L, &un->buf); +} + +static int meth_receive(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixtcp{client}", 1); + return buffer_meth_receive(L, &un->buf); +} + +static int meth_getstats(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixtcp{client}", 1); + return buffer_meth_getstats(L, &un->buf); +} + +static int meth_setstats(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixtcp{client}", 1); + return buffer_meth_setstats(L, &un->buf); +} + +/*-------------------------------------------------------------------------*\ +* Just call option handler +\*-------------------------------------------------------------------------*/ +static int meth_setoption(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixtcp{any}", 1); + return opt_meth_setoption(L, optset, &un->sock); +} + +/*-------------------------------------------------------------------------*\ +* Select support methods +\*-------------------------------------------------------------------------*/ +static int meth_getfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixtcp{any}", 1); + lua_pushnumber(L, (int) un->sock); + return 1; +} + +/* this is very dangerous, but can be handy for those that are brave enough */ +static int meth_setfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixtcp{any}", 1); + un->sock = (t_socket) luaL_checknumber(L, 2); + return 0; +} + +static int meth_dirty(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixtcp{any}", 1); + lua_pushboolean(L, !buffer_isempty(&un->buf)); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Waits for and returns a client object attempting connection to the +* server object +\*-------------------------------------------------------------------------*/ +static int meth_accept(lua_State *L) { + p_unix server = (p_unix) auxiliar_checkclass(L, "unixtcp{server}", 1); + p_timeout tm = timeout_markstart(&server->tm); + t_socket sock; + int err = socket_accept(&server->sock, &sock, NULL, NULL, tm); + /* if successful, push client socket */ + if (err == IO_DONE) { + p_unix clnt = (p_unix) lua_newuserdata(L, sizeof(t_unix)); + auxiliar_setclass(L, "unixtcp{client}", -1); + /* initialize structure fields */ + socket_setnonblocking(&sock); + clnt->sock = sock; + io_init(&clnt->io, (p_send)socket_send, (p_recv)socket_recv, + (p_error) socket_ioerror, &clnt->sock); + timeout_init(&clnt->tm, -1, -1); + buffer_init(&clnt->buf, &clnt->io, &clnt->tm); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } +} + +/*-------------------------------------------------------------------------*\ +* Binds an object to an address +\*-------------------------------------------------------------------------*/ +static const char *unixtcp_trybind(p_unix un, const char *path) { + struct sockaddr_un local; + size_t len = strlen(path); + int err; + if (len >= sizeof(local.sun_path)) return "path too long"; + memset(&local, 0, sizeof(local)); + strcpy(local.sun_path, path); + local.sun_family = AF_UNIX; +#ifdef UNIX_HAS_SUN_LEN + local.sun_len = sizeof(local.sun_family) + sizeof(local.sun_len) + + len + 1; + err = socket_bind(&un->sock, (SA *) &local, local.sun_len); + +#else + err = socket_bind(&un->sock, (SA *) &local, + sizeof(local.sun_family) + len); +#endif + if (err != IO_DONE) socket_destroy(&un->sock); + return socket_strerror(err); +} + +static int meth_bind(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixtcp{master}", 1); + const char *path = luaL_checkstring(L, 2); + const char *err = unixtcp_trybind(un, path); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +static int meth_getsockname(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixtcp{any}", 1); + struct sockaddr_un peer = {0}; + socklen_t peer_len = sizeof(peer); + + if (getsockname(un->sock, (SA *) &peer, &peer_len) < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + return 2; + } + + lua_pushstring(L, peer.sun_path); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Turns a master unixtcp object into a client object. +\*-------------------------------------------------------------------------*/ +static const char *unixtcp_tryconnect(p_unix un, const char *path) +{ + struct sockaddr_un remote; + int err; + size_t len = strlen(path); + if (len >= sizeof(remote.sun_path)) return "path too long"; + memset(&remote, 0, sizeof(remote)); + strcpy(remote.sun_path, path); + remote.sun_family = AF_UNIX; + timeout_markstart(&un->tm); +#ifdef UNIX_HAS_SUN_LEN + remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len) + + len + 1; + err = socket_connect(&un->sock, (SA *) &remote, remote.sun_len, &un->tm); +#else + err = socket_connect(&un->sock, (SA *) &remote, + sizeof(remote.sun_family) + len, &un->tm); +#endif + if (err != IO_DONE) socket_destroy(&un->sock); + return socket_strerror(err); +} + +static int meth_connect(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unixtcp{master}", 1); + const char *path = luaL_checkstring(L, 2); + const char *err = unixtcp_tryconnect(un, path); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + /* turn master object into a client object */ + auxiliar_setclass(L, "unixtcp{client}", 1); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Closes socket used by object +\*-------------------------------------------------------------------------*/ +static int meth_close(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixtcp{any}", 1); + socket_destroy(&un->sock); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Puts the sockt in listen mode +\*-------------------------------------------------------------------------*/ +static int meth_listen(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unixtcp{master}", 1); + int backlog = (int) luaL_optnumber(L, 2, 32); + int err = socket_listen(&un->sock, backlog); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } + /* turn master object into a server object */ + auxiliar_setclass(L, "unixtcp{server}", 1); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Shuts the connection down partially +\*-------------------------------------------------------------------------*/ +static int meth_shutdown(lua_State *L) +{ + /* SHUT_RD, SHUT_WR, SHUT_RDWR have the value 0, 1, 2, so we can use method index directly */ + static const char* methods[] = { "receive", "send", "both", NULL }; + p_unix tcp = (p_unix) auxiliar_checkclass(L, "unixtcp{client}", 1); + int how = luaL_checkoption(L, 2, "both", methods); + socket_shutdown(&tcp->sock, how); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Just call tm methods +\*-------------------------------------------------------------------------*/ +static int meth_settimeout(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixtcp{any}", 1); + return timeout_meth_settimeout(L, &un->tm); +} + +/*=========================================================================*\ +* Library functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Creates a master unixtcp object +\*-------------------------------------------------------------------------*/ +static int global_create(lua_State *L) { + t_socket sock; + int err = socket_create(&sock, AF_UNIX, SOCK_STREAM, 0); + /* try to allocate a system socket */ + if (err == IO_DONE) { + /* allocate unixtcp object */ + p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); + /* set its type as master object */ + auxiliar_setclass(L, "unixtcp{master}", -1); + /* initialize remaining structure fields */ + socket_setnonblocking(&sock); + un->sock = sock; + io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv, + (p_error) socket_ioerror, &un->sock); + timeout_init(&un->tm, -1, -1); + buffer_init(&un->buf, &un->io, &un->tm); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } +} diff --git a/src/unixtcp.h b/src/unixtcp.h new file mode 100644 index 0000000..0eababc --- /dev/null +++ b/src/unixtcp.h @@ -0,0 +1,21 @@ +#ifndef UNIXTCP_H +#define UNIXTCP_H +/*=========================================================================*\ +* UNIX TCP object +* LuaSocket toolkit +* +* The unixtcp.h module is basicly a glue that puts together modules buffer.h, +* timeout.h socket.h and inet.h to provide the LuaSocket UNIX TCP (AF_UNIX, +* SOCK_STREAM) support. +* +* Three classes are defined: master, client and server. The master class is +* a newly created unixtcp object, that has not been bound or connected. Server +* objects are unixtcp objects bound to some local address. Client objects are +* unixtcp objects either connected to some address or returned by the accept +* method of a server object. +\*=========================================================================*/ +#include "unix.h" + +int unixtcp_open(lua_State *L); + +#endif /* UNIXTCP_H */ diff --git a/src/unixudp.c b/src/unixudp.c new file mode 100644 index 0000000..0e0a19a --- /dev/null +++ b/src/unixudp.c @@ -0,0 +1,407 @@ +/*=========================================================================*\ +* Unix domain socket udp submodule +* LuaSocket toolkit +\*=========================================================================*/ +#include +#include + +#include "lua.h" +#include "lauxlib.h" +#include "compat.h" + +#include "auxiliar.h" +#include "socket.h" +#include "options.h" +#include "unix.h" +#include + +#define UNIXUDP_DATAGRAMSIZE 8192 + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int global_create(lua_State *L); +static int meth_connect(lua_State *L); +static int meth_bind(lua_State *L); +static int meth_send(lua_State *L); +static int meth_receive(lua_State *L); +static int meth_close(lua_State *L); +static int meth_setoption(lua_State *L); +static int meth_settimeout(lua_State *L); +static int meth_gettimeout(lua_State *L); +static int meth_getfd(lua_State *L); +static int meth_setfd(lua_State *L); +static int meth_dirty(lua_State *L); +static int meth_receivefrom(lua_State *L); +static int meth_sendto(lua_State *L); +static int meth_getsockname(lua_State *L); + +static const char *unixudp_tryconnect(p_unix un, const char *path); +static const char *unixudp_trybind(p_unix un, const char *path); + +/* unixudp object methods */ +static luaL_Reg unixudp_methods[] = { + {"__gc", meth_close}, + {"__tostring", auxiliar_tostring}, + {"bind", meth_bind}, + {"close", meth_close}, + {"connect", meth_connect}, + {"dirty", meth_dirty}, + {"getfd", meth_getfd}, + {"send", meth_send}, + {"sendto", meth_sendto}, + {"receive", meth_receive}, + {"receivefrom", meth_receivefrom}, + {"setfd", meth_setfd}, + {"setoption", meth_setoption}, + {"setpeername", meth_connect}, + {"setsockname", meth_bind}, + {"getsockname", meth_getsockname}, + {"settimeout", meth_settimeout}, + {"gettimeout", meth_gettimeout}, + {NULL, NULL} +}; + +/* socket option handlers */ +static t_opt optset[] = { + {"reuseaddr", opt_set_reuseaddr}, + {NULL, NULL} +}; + +/* functions in library namespace */ +static luaL_Reg func[] = { + {"udp", global_create}, + {NULL, NULL} +}; + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int unixudp_open(lua_State *L) +{ + /* create classes */ + auxiliar_newclass(L, "unixudp{connected}", unixudp_methods); + auxiliar_newclass(L, "unixudp{unconnected}", unixudp_methods); + /* create class groups */ + auxiliar_add2group(L, "unixudp{connected}", "unixudp{any}"); + auxiliar_add2group(L, "unixudp{unconnected}", "unixudp{any}"); + auxiliar_add2group(L, "unixudp{connected}", "select{able}"); + auxiliar_add2group(L, "unixudp{unconnected}", "select{able}"); + + luaL_setfuncs(L, func, 0); + return 0; +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +static const char *unixudp_strerror(int err) +{ + /* a 'closed' error on an unconnected means the target address was not + * accepted by the transport layer */ + if (err == IO_CLOSED) return "refused"; + else return socket_strerror(err); +} + +static int meth_send(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unixudp{connected}", 1); + p_timeout tm = &un->tm; + size_t count, sent = 0; + int err; + const char *data = luaL_checklstring(L, 2, &count); + timeout_markstart(tm); + err = socket_send(&un->sock, data, count, &sent, tm); + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, unixudp_strerror(err)); + return 2; + } + lua_pushnumber(L, (lua_Number) sent); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Send data through unconnected unixudp socket +\*-------------------------------------------------------------------------*/ +static int meth_sendto(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unixudp{unconnected}", 1); + size_t count, sent = 0; + const char *data = luaL_checklstring(L, 2, &count); + const char *path = luaL_checkstring(L, 3); + p_timeout tm = &un->tm; + int err; + struct sockaddr_un remote; + size_t len = strlen(path); + + if (len >= sizeof(remote.sun_path)) { + lua_pushnil(L); + lua_pushstring(L, "path too long"); + return 2; + } + + memset(&remote, 0, sizeof(remote)); + strcpy(remote.sun_path, path); + remote.sun_family = AF_UNIX; + timeout_markstart(tm); +#ifdef UNIX_HAS_SUN_LEN + remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len) + + len + 1; + err = socket_sendto(&un->sock, data, count, &sent, (SA *) &remote, remote.sun_len, tm); +#else + err = socket_sendto(&un->sock, data, count, &sent, (SA *) &remote, + sizeof(remote.sun_family) + len, tm); +#endif + if (err != IO_DONE) { + lua_pushnil(L); + lua_pushstring(L, unixudp_strerror(err)); + return 2; + } + lua_pushnumber(L, (lua_Number) sent); + return 1; +} + +static int meth_receive(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixudp{any}", 1); + char buf[UNIXUDP_DATAGRAMSIZE]; + size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); + char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; + int err; + p_timeout tm = &un->tm; + timeout_markstart(tm); + if (!dgram) { + lua_pushnil(L); + lua_pushliteral(L, "out of memory"); + return 2; + } + err = socket_recv(&un->sock, dgram, wanted, &got, tm); + /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ + if (err != IO_DONE && err != IO_CLOSED) { + lua_pushnil(L); + lua_pushstring(L, unixudp_strerror(err)); + if (wanted > sizeof(buf)) free(dgram); + return 2; + } + lua_pushlstring(L, dgram, got); + if (wanted > sizeof(buf)) free(dgram); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Receives data and sender from a UDP socket +\*-------------------------------------------------------------------------*/ +static int meth_receivefrom(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "unixudp{unconnected}", 1); + char buf[UNIXUDP_DATAGRAMSIZE]; + size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); + char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; + struct sockaddr_un addr; + socklen_t addr_len = sizeof(addr); + int err; + p_timeout tm = &un->tm; + timeout_markstart(tm); + if (!dgram) { + lua_pushnil(L); + lua_pushliteral(L, "out of memory"); + return 2; + } + err = socket_recvfrom(&un->sock, dgram, wanted, &got, (SA *) &addr, + &addr_len, tm); + /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ + if (err != IO_DONE && err != IO_CLOSED) { + lua_pushnil(L); + lua_pushstring(L, unixudp_strerror(err)); + if (wanted > sizeof(buf)) free(dgram); + return 2; + } + + lua_pushlstring(L, dgram, got); + /* the path may be empty, when client send without bind */ + lua_pushstring(L, addr.sun_path); + if (wanted > sizeof(buf)) free(dgram); + return 2; +} + +/*-------------------------------------------------------------------------*\ +* Just call option handler +\*-------------------------------------------------------------------------*/ +static int meth_setoption(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixudp{any}", 1); + return opt_meth_setoption(L, optset, &un->sock); +} + +/*-------------------------------------------------------------------------*\ +* Select support methods +\*-------------------------------------------------------------------------*/ +static int meth_getfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixudp{any}", 1); + lua_pushnumber(L, (int) un->sock); + return 1; +} + +/* this is very dangerous, but can be handy for those that are brave enough */ +static int meth_setfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixudp{any}", 1); + un->sock = (t_socket) luaL_checknumber(L, 2); + return 0; +} + +static int meth_dirty(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixudp{any}", 1); + (void) un; + lua_pushboolean(L, 0); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Binds an object to an address +\*-------------------------------------------------------------------------*/ +static const char *unixudp_trybind(p_unix un, const char *path) { + struct sockaddr_un local; + size_t len = strlen(path); + int err; + if (len >= sizeof(local.sun_path)) return "path too long"; + memset(&local, 0, sizeof(local)); + strcpy(local.sun_path, path); + local.sun_family = AF_UNIX; +#ifdef UNIX_HAS_SUN_LEN + local.sun_len = sizeof(local.sun_family) + sizeof(local.sun_len) + + len + 1; + err = socket_bind(&un->sock, (SA *) &local, local.sun_len); + +#else + err = socket_bind(&un->sock, (SA *) &local, + sizeof(local.sun_family) + len); +#endif + if (err != IO_DONE) socket_destroy(&un->sock); + return socket_strerror(err); +} + +static int meth_bind(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkclass(L, "unixudp{unconnected}", 1); + const char *path = luaL_checkstring(L, 2); + const char *err = unixudp_trybind(un, path); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + lua_pushnumber(L, 1); + return 1; +} + +static int meth_getsockname(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixudp{any}", 1); + struct sockaddr_un peer = {0}; + socklen_t peer_len = sizeof(peer); + + if (getsockname(un->sock, (SA *) &peer, &peer_len) < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + return 2; + } + + lua_pushstring(L, peer.sun_path); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Turns a master unixudp object into a client object. +\*-------------------------------------------------------------------------*/ +static const char *unixudp_tryconnect(p_unix un, const char *path) +{ + struct sockaddr_un remote; + int err; + size_t len = strlen(path); + if (len >= sizeof(remote.sun_path)) return "path too long"; + memset(&remote, 0, sizeof(remote)); + strcpy(remote.sun_path, path); + remote.sun_family = AF_UNIX; + timeout_markstart(&un->tm); +#ifdef UNIX_HAS_SUN_LEN + remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len) + + len + 1; + err = socket_connect(&un->sock, (SA *) &remote, remote.sun_len, &un->tm); +#else + err = socket_connect(&un->sock, (SA *) &remote, + sizeof(remote.sun_family) + len, &un->tm); +#endif + if (err != IO_DONE) socket_destroy(&un->sock); + return socket_strerror(err); +} + +static int meth_connect(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixudp{any}", 1); + const char *path = luaL_checkstring(L, 2); + const char *err = unixudp_tryconnect(un, path); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + /* turn unconnected object into a connected object */ + auxiliar_setclass(L, "unixudp{connected}", 1); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Closes socket used by object +\*-------------------------------------------------------------------------*/ +static int meth_close(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixudp{any}", 1); + socket_destroy(&un->sock); + lua_pushnumber(L, 1); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Just call tm methods +\*-------------------------------------------------------------------------*/ +static int meth_settimeout(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixudp{any}", 1); + return timeout_meth_settimeout(L, &un->tm); +} + +static int meth_gettimeout(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "unixudp{any}", 1); + return timeout_meth_gettimeout(L, &un->tm); +} + +/*=========================================================================*\ +* Library functions +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Creates a master unixudp object +\*-------------------------------------------------------------------------*/ +static int global_create(lua_State *L) +{ + t_socket sock; + int err = socket_create(&sock, AF_UNIX, SOCK_DGRAM, 0); + /* try to allocate a system socket */ + if (err == IO_DONE) { + /* allocate unixudp object */ + p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); + /* set its type as master object */ + auxiliar_setclass(L, "unixudp{unconnected}", -1); + /* initialize remaining structure fields */ + socket_setnonblocking(&sock); + un->sock = sock; + io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv, + (p_error) socket_ioerror, &un->sock); + timeout_init(&un->tm, -1, -1); + buffer_init(&un->buf, &un->io, &un->tm); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } +} diff --git a/src/unixudp.h b/src/unixudp.h new file mode 100644 index 0000000..ccfdc07 --- /dev/null +++ b/src/unixudp.h @@ -0,0 +1,20 @@ +#ifndef UNIXUDP_H +#define UNIXUDP_H +/*=========================================================================*\ +* UDP object +* LuaSocket toolkit +* +* The udp.h module provides LuaSocket with support for UDP protocol +* (AF_INET, SOCK_DGRAM). +* +* Two classes are defined: connected and unconnected. UDP objects are +* originally unconnected. They can be "connected" to a given address +* with a call to the setpeername function. The same function can be used to +* break the connection. +\*=========================================================================*/ + +#include "unix.h" + +int unixudp_open(lua_State *L); + +#endif /* UNIXUDP_H */ diff --git a/test/unixclnt.lua b/test/unixtcpclnt.lua similarity index 84% rename from test/unixclnt.lua rename to test/unixtcpclnt.lua index 5171535..652a680 100644 --- a/test/unixclnt.lua +++ b/test/unixtcpclnt.lua @@ -1,6 +1,6 @@ socket = require"socket" socket.unix = require"socket.unix" -c = assert(socket.unix()) +c = assert(socket.unix.tcp()) assert(c:connect("/tmp/foo")) while 1 do local l = io.read() diff --git a/test/unixsrvr.lua b/test/unixtcpsrvr.lua similarity index 85% rename from test/unixsrvr.lua rename to test/unixtcpsrvr.lua index 81b9c99..2a2b065 100644 --- a/test/unixsrvr.lua +++ b/test/unixtcpsrvr.lua @@ -1,6 +1,6 @@ socket = require"socket" socket.unix = require"socket.unix" - u = assert(socket.unix()) + u = assert(socket.unix.tcp()) assert(u:bind("/tmp/foo")) assert(u:listen()) c = assert(u:accept()) diff --git a/test/unixudpclnt.lua b/test/unixudpclnt.lua new file mode 100644 index 0000000..bbbff7f --- /dev/null +++ b/test/unixudpclnt.lua @@ -0,0 +1,9 @@ +socket = require"socket" +socket.unix = require"socket.unix" +c = assert(socket.unix.udp()) +c:bind("/tmp/bar") +while 1 do + local l = io.read("*l") + assert(c:sendto(l, "/tmp/foo")) + print(assert(c:receivefrom())) +end diff --git a/test/unixudpsrvr.lua b/test/unixudpsrvr.lua new file mode 100644 index 0000000..5ed71dc --- /dev/null +++ b/test/unixudpsrvr.lua @@ -0,0 +1,9 @@ + socket = require"socket" + socket.unix = require"socket.unix" + u = assert(socket.unix.udp()) + assert(u:bind("/tmp/foo")) + while 1 do + x, r = assert(u:receivefrom()) + print(x, r) + assert(u:sendto(">" .. x, r)) + end