From 1f704cfb89324fd7b7cc6f92ea7fa66c7a46846c Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Tue, 27 Sep 2011 12:54:51 -0700 Subject: Add all-unix and install-unix targets which include all modules supported on unix. Besides standard socket and mime modules, this includes unix domain socket support. --- src/makefile | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/makefile b/src/makefile index b614f77..6ec8718 100644 --- a/src/makefile +++ b/src/makefile @@ -55,6 +55,8 @@ $(SOCKET_SO): $(SOCKET_OBJS) $(MIME_SO): $(MIME_OBJS) $(LD) $(LDFLAGS) -o $@ $(MIME_OBJS) +all-unix: all $(UNIX_SO) + $(UNIX_SO): $(UNIX_OBJS) $(LD) $(LDFLAGS) -o $@ $(UNIX_OBJS) -- cgit v1.2.3-55-g6feb From 51acb54760dc91095d59839e8ea2256557f42781 Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Fri, 17 Jun 2011 13:51:34 -0700 Subject: Stop returning an error after successful send of zero length UDP packets A zero-length send is invalid with TCP, but well defined with UDP. udp:send"" was returning (nil,"refused"), indicating that it failed when the packet was actually sent. The test script reproduces the bug, and includes a tcpdump of the zero length packet being sent. --- src/usocket.c | 11 +++++------ test/udp-zero-length-send | 25 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) create mode 100755 test/udp-zero-length-send (limited to 'src') diff --git a/src/usocket.c b/src/usocket.c index ef275b4..97f8b4f 100644 --- a/src/usocket.c +++ b/src/usocket.c @@ -213,14 +213,13 @@ int socket_send(p_socket ps, const char *data, size_t count, for ( ;; ) { long put = (long) send(*ps, data, count, 0); /* if we sent anything, we are done */ - if (put > 0) { + if (put >= 0) { *sent = put; return IO_DONE; } err = errno; - /* send can't really return 0, but EPIPE means the connection was - closed */ - if (put == 0 || err == EPIPE) return IO_CLOSED; + /* EPIPE means the connection was closed */ + if (err == EPIPE) return IO_CLOSED; /* we call was interrupted, just try again */ if (err == EINTR) continue; /* if failed fatal reason, report error */ @@ -243,12 +242,12 @@ int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, if (*ps == SOCKET_INVALID) return IO_CLOSED; for ( ;; ) { long put = (long) sendto(*ps, data, count, 0, addr, len); - if (put > 0) { + if (put >= 0) { *sent = put; return IO_DONE; } err = errno; - if (put == 0 || err == EPIPE) return IO_CLOSED; + if (err == EPIPE) return IO_CLOSED; if (err == EINTR) continue; if (err != EAGAIN) return err; if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; diff --git a/test/udp-zero-length-send b/test/udp-zero-length-send new file mode 100755 index 0000000..a594944 --- /dev/null +++ b/test/udp-zero-length-send @@ -0,0 +1,25 @@ +#!/usr/bin/lua + +--[[ +Show that luasocket returns an error message on zero-length UDP sends, +even though the send is valid, and in fact the UDP packet is sent +to the peer: + +% sudo tcpdump -i lo -n +tcpdump: verbose output suppressed, use -v or -vv for full protocol decode +listening on lo, link-type EN10MB (Ethernet), capture size 65535 bytes +13:40:16.652808 IP 127.0.0.1.56573 > 127.0.0.1.5432: UDP, length 0 + +]] + +require"socket" + +s = assert(socket.udp()) +r = assert(socket.udp()) +assert(r:setsockname("*", 5432)) +assert(s:setpeername("127.0.0.1", 5432)) + +ssz, emsg = s:send("") + +print(ssz == 0 and "OK" or "FAIL",[[send:("")]], ssz, emsg) + -- cgit v1.2.3-55-g6feb From 21698c7665ee1cb43e1b83c3ea5cf4dbf827c1df Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Mon, 8 Aug 2011 16:11:47 -0700 Subject: Receive of zero for UDP is now possible. Previously, receive of zero was considered to be "closed", but that is only true for stream-based protocols, like TCP. --- src/udp.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/udp.c b/src/udp.c index e604bea..8168bf2 100644 --- a/src/udp.c +++ b/src/udp.c @@ -170,6 +170,9 @@ static int meth_receive(lua_State *L) { count = MIN(count, sizeof(buffer)); timeout_markstart(tm); err = socket_recv(&udp->sock, buffer, count, &got, tm); + /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ + if (err == IO_CLOSED) + err = IO_DONE; if (err != IO_DONE) { lua_pushnil(L); lua_pushstring(L, udp_strerror(err)); @@ -194,6 +197,9 @@ static int meth_receivefrom(lua_State *L) { count = MIN(count, sizeof(buffer)); err = socket_recvfrom(&udp->sock, buffer, count, &got, (SA *) &addr, &addr_len, tm); + /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ + if (err == IO_CLOSED) + err = IO_DONE; if (err == IO_DONE) { lua_pushlstring(L, buffer, got); lua_pushstring(L, inet_ntoa(addr.sin_addr)); -- cgit v1.2.3-55-g6feb From f63d616bc048fe256181ff5e7e4aaca11afe3237 Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Mon, 27 Jun 2011 17:04:32 -0700 Subject: Use poll by default for socket.connect(), instead of select(). Connect timeouts are implemented by waiting on the new socket descriptor. When select() is used for this, it imposes an arbitrary limit on the number of connections that can be made, usually 1024-3. Using poll() removes this limit on the number of simultaneous TCP connections can be made using luasocket. The previous default implementation using select() is available by defining SOCKET_SELECT. Note that using socket.select() always uses select(), so it isn't possible to wait on an arbitrary number of connections at once. --- src/usocket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/usocket.c b/src/usocket.c index 97f8b4f..e43cfa4 100644 --- a/src/usocket.c +++ b/src/usocket.c @@ -16,7 +16,7 @@ /*-------------------------------------------------------------------------*\ * Wait for readable/writable/connected socket with timeout \*-------------------------------------------------------------------------*/ -#ifdef SOCKET_POLL +#ifndef SOCKET_SELECT #include #define WAITFD_R POLLIN -- cgit v1.2.3-55-g6feb From b1f7c349b5714ebe304f93e43576a0ff3f721fc1 Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Thu, 23 Feb 2012 17:12:37 -0800 Subject: Add support for serial devices as socket streams on unix. --- config | 1 + makefile | 1 + src/makefile | 18 +++++- src/serial.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/socket.h | 3 + src/usocket.c | 60 +++++++++++++++++++ 6 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 src/serial.c (limited to 'src') diff --git a/config b/config index d6085ad..7d73638 100644 --- a/config +++ b/config @@ -11,6 +11,7 @@ MIME_V=1.0.2 SOCKET_SO=socket.$(EXT).$(SOCKET_V) MIME_SO=mime.$(EXT).$(MIME_V) UNIX_SO=unix.$(EXT) +SERIAL_SO=serial.$(EXT) #------ # Lua includes and libraries diff --git a/makefile b/makefile index 97a72e9..b1c9f18 100644 --- a/makefile +++ b/makefile @@ -48,6 +48,7 @@ install: all install-unix: install all-unix cd src; $(INSTALL_EXEC) $(UNIX_SO) $(INSTALL_SOCKET_LIB)/$(UNIX_SO) + cd src; $(INSTALL_EXEC) $(SERIAL_SO) $(INSTALL_SOCKET_LIB)/$(SERIAL_SO) #------ # End of makefile diff --git a/src/makefile b/src/makefile index 6ec8718..c5c22f2 100644 --- a/src/makefile +++ b/src/makefile @@ -47,6 +47,17 @@ UNIX_OBJS:=\ usocket.o \ unix.o +#------ +# Modules belonging to serial (device streams) +# +SERIAL_OBJS:=\ + buffer.o \ + auxiliar.o \ + timeout.o \ + io.o \ + usocket.o \ + serial.o + all: $(SOCKET_SO) $(MIME_SO) $(SOCKET_SO): $(SOCKET_OBJS) @@ -55,11 +66,14 @@ $(SOCKET_SO): $(SOCKET_OBJS) $(MIME_SO): $(MIME_OBJS) $(LD) $(LDFLAGS) -o $@ $(MIME_OBJS) -all-unix: all $(UNIX_SO) +all-unix: all $(UNIX_SO) $(SERIAL_SO) $(UNIX_SO): $(UNIX_OBJS) $(LD) $(LDFLAGS) -o $@ $(UNIX_OBJS) +$(SERIAL_SO): $(SERIAL_OBJS) + $(LD) $(LDFLAGS) -o $@ $(SERIAL_OBJS) + #------ # List of dependencies # @@ -74,6 +88,8 @@ mime.o: mime.c mime.h options.o: options.c auxiliar.h options.h socket.h io.h timeout.h \ usocket.h inet.h select.o: select.c socket.h io.h timeout.h usocket.h select.h +serial.o: serial.c auxiliar.h socket.h io.h timeout.h usocket.h \ + unix.h buffer.h tcp.o: tcp.c auxiliar.h socket.h io.h timeout.h usocket.h inet.h \ options.h tcp.h buffer.h timeout.o: timeout.c auxiliar.h timeout.h diff --git a/src/serial.c b/src/serial.c new file mode 100644 index 0000000..b356a3a --- /dev/null +++ b/src/serial.c @@ -0,0 +1,183 @@ +/*=========================================================================*\ +* Serial stream +* LuaSocket toolkit +\*=========================================================================*/ +#include + +#include "lua.h" +#include "lauxlib.h" + +#include "auxiliar.h" +#include "socket.h" +#include "options.h" +#include "unix.h" +#include + +/* +Reuses userdata definition from unix.h, since it is useful for all +stream-like objects. + +If we stored the serial path for use in error messages or userdata +printing, we might need our own userdata definition. + +Group usage is semi-inherited from unix.c, but unnecessary since we +have only one object type. +*/ + +/*=========================================================================*\ +* Internal function prototypes +\*=========================================================================*/ +static int global_create(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_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); + +/* serial object methods */ +static luaL_reg un[] = { + {"__gc", meth_close}, + {"__tostring", auxiliar_tostring}, + {"close", meth_close}, + {"dirty", meth_dirty}, + {"getfd", meth_getfd}, + {"getstats", meth_getstats}, + {"setstats", meth_setstats}, + {"receive", meth_receive}, + {"send", meth_send}, + {"setfd", meth_setfd}, + {"settimeout", meth_settimeout}, + {NULL, NULL} +}; + +/* our socket creation function */ +static luaL_reg func[] = { + {"serial", global_create}, + {NULL, NULL} +}; + + +/*-------------------------------------------------------------------------*\ +* Initializes module +\*-------------------------------------------------------------------------*/ +int luaopen_socket_serial(lua_State *L) { + /* create classes */ + auxiliar_newclass(L, "serial{client}", un); + /* create class groups */ + auxiliar_add2group(L, "serial{client}", "serial{any}"); + /* make sure the function ends up in the package table */ + luaL_openlib(L, "socket", func, 0); + /* return the function instead of the 'socket' table */ + lua_pushstring(L, "serial"); + lua_gettable(L, -2); + return 1; +} + +/*=========================================================================*\ +* Lua methods +\*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Just call buffered IO methods +\*-------------------------------------------------------------------------*/ +static int meth_send(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); + return buffer_meth_send(L, &un->buf); +} + +static int meth_receive(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); + return buffer_meth_receive(L, &un->buf); +} + +static int meth_getstats(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); + return buffer_meth_getstats(L, &un->buf); +} + +static int meth_setstats(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1); + return buffer_meth_setstats(L, &un->buf); +} + +/*-------------------------------------------------------------------------*\ +* Select support methods +\*-------------------------------------------------------------------------*/ +static int meth_getfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{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, "serial{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, "serial{any}", 1); + lua_pushboolean(L, !buffer_isempty(&un->buf)); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Closes socket used by object +\*-------------------------------------------------------------------------*/ +static int meth_close(lua_State *L) +{ + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{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, "serial{any}", 1); + return timeout_meth_settimeout(L, &un->tm); +} + +/*=========================================================================*\ +* Library functions +\*=========================================================================*/ + + +/*-------------------------------------------------------------------------*\ +* Creates a serial object +\*-------------------------------------------------------------------------*/ +static int global_create(lua_State *L) { + const char* path = luaL_checkstring(L, 1); + + /* allocate unix object */ + p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); + + /* open serial device */ + t_socket sock = open(path, O_NOCTTY|O_RDWR); + + /*printf("open %s on %d\n", path, sock);*/ + + if (sock < 0) { + lua_pushnil(L); + lua_pushstring(L, socket_strerror(errno)); + lua_pushnumber(L, errno); + return 3; + } + /* set its type as client object */ + auxiliar_setclass(L, "serial{client}", -1); + /* initialize remaining structure fields */ + socket_setnonblocking(&sock); + un->sock = sock; + io_init(&un->io, (p_send) socket_write, (p_recv) socket_read, + (p_error) socket_ioerror, &un->sock); + timeout_init(&un->tm, -1, -1); + buffer_init(&un->buf, &un->io, &un->tm); + return 1; +} diff --git a/src/socket.h b/src/socket.h index de5d79f..debb13a 100644 --- a/src/socket.h +++ b/src/socket.h @@ -68,6 +68,9 @@ const char *socket_strerror(int err); int socket_send(p_socket ps, const char *data, size_t count, size_t *sent, p_timeout tm); int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm); +int socket_write(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm); +int socket_read(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm); const char *socket_ioerror(p_socket ps, int err); int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp); diff --git a/src/usocket.c b/src/usocket.c index e43cfa4..46087c6 100644 --- a/src/usocket.c +++ b/src/usocket.c @@ -300,6 +300,66 @@ int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, return IO_UNKNOWN; } + +/*-------------------------------------------------------------------------*\ +* Write with timeout +* +* socket_read and socket_write are cut-n-paste of socket_send and socket_recv, +* with send/recv replaced with write/read. We can't just use write/read +* in the socket version, because behaviour when size is zero is different. +\*-------------------------------------------------------------------------*/ +int socket_write(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm) +{ + int err; + *sent = 0; + /* avoid making system calls on closed sockets */ + if (*ps == SOCKET_INVALID) return IO_CLOSED; + /* loop until we send something or we give up on error */ + for ( ;; ) { + long put = (long) write(*ps, data, count); + /* if we sent anything, we are done */ + if (put >= 0) { + *sent = put; + return IO_DONE; + } + err = errno; + /* EPIPE means the connection was closed */ + if (err == EPIPE) return IO_CLOSED; + /* we call was interrupted, just try again */ + if (err == EINTR) continue; + /* if failed fatal reason, report error */ + if (err != EAGAIN) return err; + /* wait until we can send something or we timeout */ + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; + } + /* can't reach here */ + return IO_UNKNOWN; +} + +/*-------------------------------------------------------------------------*\ +* Read with timeout +* See note for socket_write +\*-------------------------------------------------------------------------*/ +int socket_read(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) { + int err; + *got = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + long taken = (long) read(*ps, data, count); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + err = errno; + if (taken == 0) return IO_CLOSED; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; +} + /*-------------------------------------------------------------------------*\ * Put socket into blocking mode \*-------------------------------------------------------------------------*/ -- cgit v1.2.3-55-g6feb From e716e7347b35ab0d00d95308522c1a099a01ebaa Mon Sep 17 00:00:00 2001 From: root Date: Sun, 8 Jan 2012 12:34:11 -0300 Subject: mime: Remove io dependency This dependency was spuriously added, maybe for debug reasons, as confirmed to me by Diego Nehab by mail. Some systems based in Lua (e.g. Ginga) prohibit the use of io module for security reasons, so this dependency makes mime unusable; even worse this makes other modules, based on mime, unusable too (e.g. html). --- src/mime.lua | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/mime.lua b/src/mime.lua index eb75db2..81d8e0d 100644 --- a/src/mime.lua +++ b/src/mime.lua @@ -11,7 +11,6 @@ local base = _G local ltn12 = require("ltn12") local mime = require("mime.core") -local io = require("io") local string = require("string") module("mime") -- cgit v1.2.3-55-g6feb From 27a3964ff7891a5f240ef08b3294714ef6e28074 Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Tue, 21 Feb 2012 13:11:39 -0800 Subject: socket_strerror() sometimes maps errno, instead of its argument Looks like a historical bug. Its err argument is an error number, but if it isn't using a custom error message for it, it just calls strerror() with the errno global, effectively ignoring its argument and returning a semi-random string. --- src/usocket.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/usocket.c b/src/usocket.c index 46087c6..3221cb9 100644 --- a/src/usocket.c +++ b/src/usocket.c @@ -418,8 +418,8 @@ const char *socket_strerror(int err) { case ECONNREFUSED: return "connection refused"; case ECONNABORTED: return "closed"; case ECONNRESET: return "closed"; - case ETIMEDOUT: return "timedout"; - default: return strerror(errno); + case ETIMEDOUT: return "timeout"; + default: return strerror(err); } } -- cgit v1.2.3-55-g6feb From 0716cb868e847bb9f66c659f8662d905ba012de8 Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Tue, 21 Feb 2012 13:13:46 -0800 Subject: Bounds check the argument to FD_SET Failing to check the FD_SET argument against FD_SETSIZE causes undefined behaviour (segfaults, for example). --- src/usocket.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/usocket.c b/src/usocket.c index 3221cb9..f4c0a84 100644 --- a/src/usocket.c +++ b/src/usocket.c @@ -49,6 +49,7 @@ int socket_waitfd(p_socket ps, int sw, p_timeout tm) { fd_set rfds, wfds, *rp, *wp; struct timeval tv, *tp; double t; + if (*ps >= FD_SETSIZE) return EINVAL; if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ do { /* must set bits within loop, because select may have modifed them */ -- cgit v1.2.3-55-g6feb From 8bb542baaf30874479b83d37af2fea5fa84d0a8e Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Mon, 27 Feb 2012 13:26:23 -0800 Subject: Support getoption method for tcp objects. --- doc/reference.html | 1 + doc/tcp.html | 35 ++++++++++++++++++++++++++- src/options.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/options.h | 8 ++++++ src/tcp.c | 16 ++++++++++++ test/tcp-getoptions | 41 +++++++++++++++++++++++++++++++ 6 files changed, 170 insertions(+), 1 deletion(-) create mode 100755 test/tcp-getoptions (limited to 'src') diff --git a/doc/reference.html b/doc/reference.html index 078d40c..d7e0923 100644 --- a/doc/reference.html +++ b/doc/reference.html @@ -172,6 +172,7 @@ Support, Manual"> connect, dirty, getfd, +getoption, getpeername, getsockname, getstats, diff --git a/doc/tcp.html b/doc/tcp.html index 9583b1f..11a0428 100644 --- a/doc/tcp.html +++ b/doc/tcp.html @@ -396,7 +396,40 @@ disables the Nagle's algorithm for the connection.

-The method returns 1 in case of success, or nil otherwise. +The method returns 1 in case of success, or nil +followed by an error message otherwise. +

+ +

+Note: The descriptions above come from the man pages. +

+ + + +

+client:getoption(option)
+server:getoption(option) +

+ +

+Gets options for the TCP object. +See setoption for description of the +option names and values. +

+ +

+Option is a string with the option name. +

    + +
  • 'keepalive' +
  • 'linger' +
  • 'reuseaddr' +
  • 'tcp-nodelay' +
+ +

+The method returns the option value in case of success, or +nil followed by an error message otherwise.

diff --git a/src/options.c b/src/options.c index a464a4b..2085fdc 100644 --- a/src/options.c +++ b/src/options.c @@ -18,8 +18,11 @@ \*=========================================================================*/ static int opt_setmembership(lua_State *L, p_socket ps, int level, int name); static int opt_setboolean(lua_State *L, p_socket ps, int level, int name); +static int opt_getboolean(lua_State *L, p_socket ps, int level, int name); static int opt_set(lua_State *L, p_socket ps, int level, int name, void *val, int len); +static int opt_get(lua_State *L, p_socket ps, int level, int name, + void *val, int* len); /*=========================================================================*\ * Exported functions @@ -40,23 +43,51 @@ int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps) return opt->func(L, ps); } +int opt_meth_getoption(lua_State *L, p_opt opt, p_socket 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_socket ps) { return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEADDR); } +int opt_get_reuseaddr(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, SOL_SOCKET, SO_REUSEADDR); +} + /* disables the Naggle algorithm */ int opt_tcp_nodelay(lua_State *L, p_socket ps) { return opt_setboolean(L, ps, IPPROTO_TCP, TCP_NODELAY); } +int opt_get_tcp_nodelay(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, IPPROTO_TCP, TCP_NODELAY); +} + int opt_keepalive(lua_State *L, p_socket ps) { return opt_setboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE); } +int opt_get_keepalive(lua_State *L, p_socket ps) +{ + return opt_getboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE); +} + int opt_dontroute(lua_State *L, p_socket ps) { return opt_setboolean(L, ps, SOL_SOCKET, SO_DONTROUTE); @@ -105,6 +136,21 @@ int opt_ip_drop_membersip(lua_State *L, p_socket ps) return opt_setmembership(L, ps, IPPROTO_IP, IP_DROP_MEMBERSHIP); } +int opt_get_linger(lua_State *L, p_socket ps) +{ + struct linger li; /* obj, name */ + int len = sizeof(li); + int err = opt_get(L, ps, SOL_SOCKET, SO_LINGER, (char *) &li, &len); + if (err) + return err; + lua_newtable(L); + lua_pushboolean(L, li.l_onoff); + lua_setfield(L, -2, "on"); + lua_pushinteger(L, li.l_linger); + lua_setfield(L, -2, "timeout"); + return 1; +} + /*=========================================================================*\ * Auxiliar functions \*=========================================================================*/ @@ -129,6 +175,19 @@ static int opt_setmembership(lua_State *L, p_socket ps, int level, int name) return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); } +static +int opt_get(lua_State *L, p_socket ps, int level, int name, void *val, int* len) +{ + socklen_t socklen = *len; + if (getsockopt(*ps, level, name, (char *) val, &socklen) < 0) { + lua_pushnil(L); + lua_pushstring(L, "getsockopt failed"); + return 2; + } + *len = socklen; + return 0; +} + static int opt_set(lua_State *L, p_socket ps, int level, int name, void *val, int len) { @@ -141,6 +200,17 @@ int opt_set(lua_State *L, p_socket ps, int level, int name, void *val, int len) return 1; } +static int opt_getboolean(lua_State *L, p_socket ps, int level, int name) +{ + int val = 0; + int len = sizeof(val); + int err = opt_get(L, ps, level, name, (char *) &val, &len); + if (err) + return err; + lua_pushboolean(L, val); + return 1; +} + static int opt_setboolean(lua_State *L, p_socket ps, int level, int name) { int val = auxiliar_checkboolean(L, 3); /* obj, name, bool */ diff --git a/src/options.h b/src/options.h index 900761e..c9b2e47 100644 --- a/src/options.h +++ b/src/options.h @@ -17,10 +17,17 @@ typedef struct t_opt { const char *name; int (*func)(lua_State *L, p_socket ps); + int (*get)(lua_State *L, p_socket ps); } t_opt; typedef t_opt *p_opt; /* supported options */ +int opt_get_reuseaddr(lua_State *L, p_socket ps); +int opt_get_tcp_nodelay(lua_State *L, p_socket ps); +int opt_get_keepalive(lua_State *L, p_socket ps); +int opt_get_linger(lua_State *L, p_socket ps); +int opt_get_reuseaddr(lua_State *L, p_socket ps); + int opt_dontroute(lua_State *L, p_socket ps); int opt_broadcast(lua_State *L, p_socket ps); int opt_reuseaddr(lua_State *L, p_socket ps); @@ -35,5 +42,6 @@ int opt_ip_drop_membersip(lua_State *L, p_socket ps); /* invokes the appropriate option handler */ int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps); +int opt_meth_getoption(lua_State *L, p_opt opt, p_socket ps); #endif diff --git a/src/tcp.c b/src/tcp.c index 539ad5e..4713b23 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -31,6 +31,7 @@ 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_getoption(lua_State *L); static int meth_setoption(lua_State *L); static int meth_settimeout(lua_State *L); static int meth_getfd(lua_State *L); @@ -47,6 +48,7 @@ static luaL_reg tcp[] = { {"connect", meth_connect}, {"dirty", meth_dirty}, {"getfd", meth_getfd}, + {"getoption", meth_getoption}, {"getpeername", meth_getpeername}, {"getsockname", meth_getsockname}, {"getstats", meth_getstats}, @@ -64,6 +66,14 @@ static luaL_reg tcp[] = { }; /* socket option handlers */ +static t_opt optget[] = { + {"keepalive", opt_get_keepalive}, + {"reuseaddr", opt_get_reuseaddr}, + {"tcp-nodelay", opt_get_tcp_nodelay}, + {"linger", opt_get_linger}, + {NULL, NULL} +}; + static t_opt opt[] = { {"keepalive", opt_keepalive}, {"reuseaddr", opt_reuseaddr}, @@ -125,6 +135,12 @@ static int meth_setstats(lua_State *L) { /*-------------------------------------------------------------------------*\ * Just call option handler \*-------------------------------------------------------------------------*/ +static int meth_getoption(lua_State *L) +{ + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + return opt_meth_getoption(L, optget, &tcp->sock); +} + static int meth_setoption(lua_State *L) { p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); diff --git a/test/tcp-getoptions b/test/tcp-getoptions new file mode 100755 index 0000000..f9b3d1b --- /dev/null +++ b/test/tcp-getoptions @@ -0,0 +1,41 @@ +#!/usr/bin/env lua + +require"socket" + +port = 8765 + +function options(o) + print("options for", o) + + for _, opt in ipairs{"keepalive", "reuseaddr", "tcp-nodelay"} do + print("getoption", opt, o:getoption(opt)) + end + + print("getoption", "linger", + "on", o:getoption("linger").on, + "timeout", o:getoption("linger").timeout) +end + +local m = socket.tcp() + +options(m) + +assert(m:bind("*", port)) +assert(m:listen()) + +options(m) + +m:close() + +local m = socket.bind("*", port) + +options(m) + +local c = socket.connect("localhost", port) + +options(c) + +local s = m:accept() + +options(s) + -- cgit v1.2.3-55-g6feb