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. --- test/udp-zero-length-send | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100755 test/udp-zero-length-send (limited to 'test') 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 c37f71d062379ff4f48658cddd724b94df20fb66 Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Mon, 8 Aug 2011 16:49:20 -0700 Subject: Test showing failure to receive a zero-length packet. --- test/udp-zero-length-send-recv | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100755 test/udp-zero-length-send-recv (limited to 'test') diff --git a/test/udp-zero-length-send-recv b/test/udp-zero-length-send-recv new file mode 100755 index 0000000..7d76c98 --- /dev/null +++ b/test/udp-zero-length-send-recv @@ -0,0 +1,35 @@ +#!/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)) + +ok, emsg = s:send("") +if ok ~= 0 then + print("send of zero failed with:", ok, emsg) +end + +ok, emsg = r:receive() + +if not ok or string.len(ok) ~= 0 then + print("receive of zero failed with:", ok, emsg) +end + +print"ok" + + -- cgit v1.2.3-55-g6feb From dace50628c5acc0aad94538eb6d3bd31e055d941 Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Tue, 28 Jun 2011 17:48:21 -0700 Subject: Utility to find how many TCP connections can be made. --- test/find-connect-limit | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100755 test/find-connect-limit (limited to 'test') diff --git a/test/find-connect-limit b/test/find-connect-limit new file mode 100755 index 0000000..ad0c3f5 --- /dev/null +++ b/test/find-connect-limit @@ -0,0 +1,32 @@ +#!/usr/bin/env lua +--[[ +Find out how many TCP connections we can make. + +Use ulimit to increase the max number of descriptors: + +ulimit -n 10000 +ulimit -n + +You'll probably need to be root to do this. +]] + +require "socket" + +host = arg[1] or "google.com" +port = arg[2] or 80 + +connections = {} + +repeat + c = assert(socket.connect(hostip or host, 80)) + table.insert(connections, c) + + if not hostip then + hostip = c:getpeername() + print("resolved", host, "to", hostip) + end + + print("connection #", #connections, c, "fd", c:getfd()) + +until false + -- cgit v1.2.3-55-g6feb From 3b19f2a7edbcde798a9cf5f1f6175d360e891744 Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Mon, 8 Aug 2011 16:23:06 -0700 Subject: testsrvr asserts when test finishes successfully. When the test client finishes, the test server asserts with a "closed" message. After looking carefully at this, I think the tests are running successfully and passing. Since it appears to be a test failure, I modified the server to allow the client to close the control connection. --- test/testsrvr.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/testsrvr.lua b/test/testsrvr.lua index f1972c2..4be4069 100644 --- a/test/testsrvr.lua +++ b/test/testsrvr.lua @@ -7,7 +7,12 @@ while 1 do print("server: waiting for client connection..."); control = assert(server:accept()); while 1 do - command = assert(control:receive()); + command, emsg = control:receive(); + if emsg == "closed" then + control:close() + break + end + assert(command, emsg) assert(control:send(ack)); print(command); (loadstring(command))(); -- 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 'test') 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 From 195b2a74bb3f368b1f31f9c8bbc1ce0f54de2035 Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Wed, 29 Feb 2012 16:55:55 -0800 Subject: On failure, exit with a message instead of blocking indefinitely. --- test/udp-zero-length-send-recv | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/udp-zero-length-send-recv b/test/udp-zero-length-send-recv index 7d76c98..541efd4 100755 --- a/test/udp-zero-length-send-recv +++ b/test/udp-zero-length-send-recv @@ -24,12 +24,14 @@ if ok ~= 0 then print("send of zero failed with:", ok, emsg) end +assert(r:settimeout(2)) + ok, emsg = r:receive() if not ok or string.len(ok) ~= 0 then - print("receive of zero failed with:", ok, emsg) + print("fail - receive of zero failed with:", ok, emsg) + os.exit(1) end print"ok" - -- cgit v1.2.3-55-g6feb