From 96965b179c7311f850f72a8629b9ba6d3a31d117 Mon Sep 17 00:00:00 2001 From: Diego Nehab Date: Sat, 22 Aug 2015 19:52:01 -0300 Subject: New agnostic IPv4 IPv6 functions. Also dealing with EPROTOTYPE Yosemite seems to be throwing at us for no reason. --- test/testclnt.lua | 100 ++++++++++++++++++++++++------------------------ test/testsrvr.lua | 2 +- test/udpconnectclnt.lua | 2 +- 3 files changed, 51 insertions(+), 53 deletions(-) (limited to 'test') diff --git a/test/testclnt.lua b/test/testclnt.lua index 0014781..abf9608 100644 --- a/test/testclnt.lua +++ b/test/testclnt.lua @@ -8,7 +8,7 @@ function printf(...) end function pass(...) - printf(...) + printf(...) io.stderr:write("\n") end @@ -45,30 +45,30 @@ function check_timeout(tm, sl, elapsed, err, opp, mode, alldone) if not err then warn("must be buffered") elseif err == "timeout" then pass("proper timeout") else fail("unexpected error '%s'", err) end - else - if err ~= "timeout" then fail("should have timed out") + else + if err ~= "timeout" then fail("should have timed out") else pass("proper timeout") end end else if mode == "total" then - if elapsed > tm then + if elapsed > tm then if err ~= "timeout" then fail("should have timed out") else pass("proper timeout") end elseif elapsed < tm then - if err then fail(err) + if err then fail(err) else pass("ok") end - else - if alldone then - if err then fail("unexpected error '%s'", err) + else + if alldone then + if err then fail("unexpected error '%s'", err) else pass("ok") end else - if err ~= "timeout" then fail(err) + if err ~= "timeout" then fail(err) else pass("proper timeoutk") end end end - else - if err then fail(err) - else pass("ok") end + else + if err then fail(err) + else pass("ok") end end end end @@ -104,8 +104,8 @@ control:setoption("tcp-nodelay", true) ------------------------------------------------------------------------ function test_methods(sock, methods) for _, v in pairs(methods) do - if type(sock[v]) ~= "function" then - fail(sock.class .. " method '" .. v .. "' not registered") + if type(sock[v]) ~= "function" then + fail(sock.class .. " method '" .. v .. "' not registered") end end pass(sock.class .. " methods are ok") @@ -121,7 +121,7 @@ function test_mixed(len) local p3 = "raw " .. string.rep("z", inter) .. "bytes" local p4 = "end" .. string.rep("w", inter) .. "bytes" local bp1, bp2, bp3, bp4 -remote (string.format("str = data:receive(%d)", +remote (string.format("str = data:receive(%d)", string.len(p1)+string.len(p2)+string.len(p3)+string.len(p4))) sent, err = data:send(p1..p2..p3..p4) if err then fail(err) end @@ -166,7 +166,7 @@ function test_rawline(len) io.stderr:write("length " .. len .. ": ") local str, str10, back, err str = string.rep(string.char(47), math.mod(len, 10)) - str10 = string.rep(string.char(120,21,77,4,5,0,7,36,44,100), + str10 = string.rep(string.char(120,21,77,4,5,0,7,36,44,100), math.floor(len/10)) str = str .. str10 remote "str = data:receive()" @@ -216,7 +216,7 @@ function test_totaltimeoutreceive(len, tm, sl) data:settimeout(tm, "total") local t = socket.gettime() str, err, partial, elapsed = data:receive(2*len) - check_timeout(tm, sl, elapsed, err, "receive", "total", + check_timeout(tm, sl, elapsed, err, "receive", "total", string.len(str or partial) == 2*len) end @@ -236,7 +236,7 @@ function test_totaltimeoutsend(len, tm, sl) data:settimeout(tm, "total") str = string.rep("a", 2*len) total, err, partial, elapsed = data:send(str) - check_timeout(tm, sl, elapsed, err, "send", "total", + check_timeout(tm, sl, elapsed, err, "send", "total", total == 2*len) end @@ -256,7 +256,7 @@ function test_blockingtimeoutreceive(len, tm, sl) ]], 2*tm, len, sl, sl)) data:settimeout(tm) str, err, partial, elapsed = data:receive(2*len) - check_timeout(tm, sl, elapsed, err, "receive", "blocking", + check_timeout(tm, sl, elapsed, err, "receive", "blocking", string.len(str or partial) == 2*len) end @@ -290,10 +290,10 @@ function empty_connect() data = server:accept() ]] data, err = socket.connect("", port) - if not data then + if not data then pass("ok") data = socket.connect(host, port) - else + else pass("gethostbyname returns localhost on empty string...") end end @@ -327,7 +327,7 @@ function test_closed() data:close() data = nil ]], str)) - -- try to get a line + -- try to get a line back, err, partial = data:receive() if not err then fail("should have gotten 'closed'.") elseif err ~= "closed" then fail("got '"..err.."' instead of 'closed'.") @@ -340,25 +340,25 @@ function test_closed() data = nil ]] total, err, partial = data:send(string.rep("ugauga", 100000)) - if not err then + if not err then pass("failed: output buffer is at least %d bytes long!", total) - elseif err ~= "closed" then + elseif err ~= "closed" then fail("got '"..err.."' instead of 'closed'.") - else - pass("graceful 'closed' received after %d bytes were sent", partial) + else + pass("graceful 'closed' received after %d bytes were sent", partial) end end ------------------------------------------------------------------------ function test_selectbugs() local r, s, e = socket.select(nil, nil, 0.1) - assert(type(r) == "table" and type(s) == "table" and + assert(type(r) == "table" and type(s) == "table" and (e == "timeout" or e == "error")) pass("both nil: ok") local udp = socket.udp() udp:close() r, s, e = socket.select({ udp }, { udp }, 0.1) - assert(type(r) == "table" and type(s) == "table" and + assert(type(r) == "table" and type(s) == "table" and (e == "timeout" or e == "error")) pass("closed sockets: ok") e = pcall(socket.select, "wrong", 1, 0.1) @@ -389,7 +389,7 @@ function accept_timeout() local t = socket.gettime() s:settimeout(1) local c, e = s:accept() - assert(not c, "should not accept") + assert(not c, "should not accept") assert(e == "timeout", string.format("wrong error message (%s)", e)) t = socket.gettime() - t assert(t < 2, string.format("took to long to give up (%gs)", t)) @@ -407,9 +407,9 @@ function connect_timeout() local t = socket.gettime() local r, e = c:connect("10.0.0.1", 81) assert(not r, "should not connect") - assert(socket.gettime() - t < 2, "took too long to give up.") + assert(socket.gettime() - t < 2, "took too long to give up.") c:close() - pass("ok") + pass("ok") end ------------------------------------------------------------------------ @@ -447,16 +447,14 @@ end ------------------------------------------------------------------------ function rebind_test() - --local c ,c1 = socket.bind("localhost", 0) local c ,c1 = socket.bind("127.0.0.1", 0) if not c then pass ("failed to bind! " .. tostring(c) .. ' ' .. tostring(c1)) return end assert(c,c1) - local i, p = c:getsockname() local s, e = socket.tcp() assert(s, e) s:setoption("reuseaddr", false) - r, e = s:bind("localhost", p) + r, e = s:bind(i, p) assert(not r, "managed to rebind!") assert(e) pass("ok") @@ -476,9 +474,9 @@ function getstats_test() data:receive(c) t = t + c local r, s, a = data:getstats() - assert(r == t, "received count failed" .. tostring(r) + assert(r == t, "received count failed" .. tostring(r) .. "/" .. tostring(t)) - assert(s == t, "sent count failed" .. tostring(s) + assert(s == t, "sent count failed" .. tostring(s) .. "/" .. tostring(t)) end pass("ok") @@ -486,7 +484,7 @@ end ------------------------------------------------------------------------ -function test_nonblocking(size) +function test_nonblocking(size) reconnect() printf("testing " .. 2*size .. " bytes: ") remote(string.format([[ @@ -545,7 +543,7 @@ function test_readafterclose() data:close() data = nil ]])) - data:close() + data:close() back, err, partial = data:receive("*a") assert(back == nil and err == "closed", "should have returned 'closed'") pass("ok") @@ -555,7 +553,7 @@ function test_readafterclose() data:close() data = nil ]])) - data:close() + data:close() back, err, partial = data:receive() assert(back == nil and err == "closed", "should have returned 'closed'") pass("ok") @@ -565,7 +563,7 @@ function test_readafterclose() data:close() data = nil ]])) - data:close() + data:close() back, err, partial = data:receive(1) assert(back == nil and err == "closed", "should have returned 'closed'") pass("ok") @@ -575,7 +573,7 @@ function test_readafterclose() data:close() data = nil ]])) - data:close() + data:close() back, err, partial = data:receive(0) assert(back == nil and err == "closed", "should have returned 'closed'") pass("ok") @@ -593,7 +591,7 @@ function test_writeafterclose() while not err do sent, err, errsent, time = data:send(str) end - assert(err == "closed", "should have returned 'closed'") + assert(err == "closed", "got " .. err .. " instead of 'closed'") pass("ok") end @@ -648,18 +646,18 @@ else io.stderr:write("Warning! IPv6 does not support!\n") end end local udp_methods = { - "close", + "close", "dirty", "getfamily", "getfd", "getoption", "getpeername", "getsockname", - "receive", - "receivefrom", - "send", - "sendto", - "setfd", + "receive", + "receivefrom", + "send", + "sendto", + "setfd", "setoption", "setpeername", "setsockname", @@ -674,6 +672,9 @@ if sock then test_methods(socket.udp6(), udp_methods) else io.stderr:write("Warning! IPv6 does not support!\n") end end +test("closed connection detection: ") +test_closed() + test("partial receive") test_partialrecv() @@ -697,9 +698,6 @@ rebind_test() test("active close: ") active_close() -test("closed connection detection: ") -test_closed() - test("accept function: ") accept_timeout() accept_errors() diff --git a/test/testsrvr.lua b/test/testsrvr.lua index 72b93ab..1eb2d5b 100644 --- a/test/testsrvr.lua +++ b/test/testsrvr.lua @@ -6,7 +6,7 @@ ack = "\n"; while 1 do print("server: waiting for client connection..."); control = assert(server:accept()); - while 1 do + while 1 do command, emsg = control:receive(); if emsg == "closed" then control:close() diff --git a/test/udpconnectclnt.lua b/test/udpconnectclnt.lua index effe13a..ad6ab6a 100644 --- a/test/udpconnectclnt.lua +++ b/test/udpconnectclnt.lua @@ -1,7 +1,7 @@ local socket = require"socket" local udp = socket.udp local localhost = "127.0.0.1" -local port = arg[1] +local port = assert(arg[1], "missing port argument") se = udp(); se:setoption("reuseaddr", true) se:setsockname(localhost, 5062) -- cgit v1.2.3-55-g6feb From 77bba625d7aaa0f9e118879163687fcbcb0b5a7b Mon Sep 17 00:00:00 2001 From: Diego Nehab Date: Tue, 25 Aug 2015 15:41:40 -0300 Subject: Fixes suggested by @Florob in #147. --- doc/tcp.html | 6 ++++++ doc/udp.html | 6 ++++++ src/inet.c | 17 +++++++++++------ src/inet.h | 2 +- src/tcp.c | 52 +++++++++++++++++++++------------------------------- src/udp.c | 52 +++++++++++++++++++++------------------------------- test/testclnt.lua | 19 ++++++++++++------- 7 files changed, 78 insertions(+), 76 deletions(-) (limited to 'test') diff --git a/doc/tcp.html b/doc/tcp.html index 6fc9900..fb627a1 100644 --- a/doc/tcp.html +++ b/doc/tcp.html @@ -65,6 +65,12 @@ href=#bind>connect, depending on the address family obtained from the resolver.

+

+Note: Before the choice between IPv4 and IPv6 happens, +the internal socket object is invalid and therefore setoption will fail. +

+

diff --git a/doc/udp.html b/doc/udp.html index e313af4..a300f2f 100644 --- a/doc/udp.html +++ b/doc/udp.html @@ -76,6 +76,12 @@ href=#setsockname>sockname, depending on the address family obtained from the resolver.

+

+Note: Before the choice between IPv4 and IPv6 happens, +the internal socket object is invalid and therefore setoption will fail. +

+

diff --git a/src/inet.c b/src/inet.c index 8f0fac2..331b800 100644 --- a/src/inet.c +++ b/src/inet.c @@ -352,8 +352,13 @@ static void inet_pushresolved(lua_State *L, struct hostent *hp) /*-------------------------------------------------------------------------*\ * Tries to create a new inet socket \*-------------------------------------------------------------------------*/ -const char *inet_trycreate(p_socket ps, int family, int type) { - return socket_strerror(socket_create(ps, family, type, 0)); +const char *inet_trycreate(p_socket ps, int family, int type, int protocol) { + const char *err = socket_strerror(socket_create(ps, family, type, protocol)); + if (err == NULL && family == AF_INET6) { + int yes = 1; + setsockopt(*ps, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&yes, sizeof(yes)); + } + return err; } /*-------------------------------------------------------------------------*\ @@ -408,8 +413,8 @@ const char *inet_tryconnect(p_socket ps, int *family, const char *address, * not enter this branch. */ if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) { socket_destroy(ps); - err = socket_strerror(socket_create(ps, iterator->ai_family, - iterator->ai_socktype, iterator->ai_protocol)); + err = inet_trycreate(ps, iterator->ai_family, + iterator->ai_socktype, iterator->ai_protocol); if (err) continue; current_family = iterator->ai_family; /* set non-blocking before connect */ @@ -466,8 +471,8 @@ const char *inet_trybind(p_socket ps, int *family, const char *address, for (iterator = resolved; iterator; iterator = iterator->ai_next) { if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) { socket_destroy(ps); - err = socket_strerror(socket_create(ps, iterator->ai_family, - iterator->ai_socktype, iterator->ai_protocol)); + err = inet_trycreate(ps, iterator->ai_family, + iterator->ai_socktype, iterator->ai_protocol); if (err) continue; current_family = iterator->ai_family; } diff --git a/src/inet.h b/src/inet.h index b85c20e..feb3541 100644 --- a/src/inet.h +++ b/src/inet.h @@ -24,7 +24,7 @@ int inet_open(lua_State *L); -const char *inet_trycreate(p_socket ps, int family, int type); +const char *inet_trycreate(p_socket ps, int family, int type, int protocol); const char *inet_tryconnect(p_socket ps, int *family, const char *address, const char *serv, p_timeout tm, struct addrinfo *connecthints); const char *inet_trybind(p_socket ps, int *family, const char *address, diff --git a/src/tcp.c b/src/tcp.c index 4d12f08..7bf1af5 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -355,39 +355,29 @@ static int meth_settimeout(lua_State *L) * Creates a master tcp object \*-------------------------------------------------------------------------*/ static int tcp_create(lua_State *L, int family) { - t_socket sock; - /* if family is AF_UNSPEC, we create an AF_INET socket - * but store AF_UNSPEC into tcp-family. This will allow it - * later be replaced with an AF_INET6 socket if - * trybind or tryconnect prefer it instead. */ - const char *err = inet_trycreate(&sock, family == AF_UNSPEC? - AF_INET: family, SOCK_STREAM); - /* try to allocate a system socket */ - if (!err) { - /* allocate tcp object */ - p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); - memset(tcp, 0, sizeof(t_tcp)); - /* set its type as master object */ - auxiliar_setclass(L, "tcp{master}", -1); - /* initialize remaining structure fields */ - socket_setnonblocking(&sock); - if (family == AF_INET6) { - int yes = 1; - setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, - (void *)&yes, sizeof(yes)); + p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); + memset(tcp, 0, sizeof(t_tcp)); + /* set its type as master object */ + auxiliar_setclass(L, "tcp{master}", -1); + /* if family is AF_UNSPEC, we leave the socket invalid and + * store AF_UNSPEC into family. This will allow it to later be + * replaced with an AF_INET6 or AF_INET socket upon first use. */ + tcp->sock = SOCKET_INVALID; + tcp->family = family; + io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv, + (p_error) socket_ioerror, &tcp->sock); + timeout_init(&tcp->tm, -1, -1); + buffer_init(&tcp->buf, &tcp->io, &tcp->tm); + if (family != AF_UNSPEC) { + const char *err = inet_trycreate(&tcp->sock, family, SOCK_STREAM, 0); + if (err != NULL) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; } - tcp->sock = sock; - io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv, - (p_error) socket_ioerror, &tcp->sock); - timeout_init(&tcp->tm, -1, -1); - buffer_init(&tcp->buf, &tcp->io, &tcp->tm); - tcp->family = family; - return 1; - } else { - lua_pushnil(L); - lua_pushstring(L, err); - return 2; + socket_setnonblocking(&tcp->sock); } + return 1; } static int global_create(lua_State *L) { diff --git a/src/udp.c b/src/udp.c index 6600859..17d932a 100644 --- a/src/udp.c +++ b/src/udp.c @@ -185,7 +185,7 @@ static int meth_sendto(lua_State *L) { return 2; } timeout_markstart(tm); - err = socket_sendto(&udp->sock, data, count, &sent, ai->ai_addr, + err = socket_sendto(&udp->sock, data, count, &sent, ai->ai_addr, (socklen_t) ai->ai_addrlen, tm); freeaddrinfo(ai); if (err != IO_DONE) { @@ -237,7 +237,7 @@ static int meth_receivefrom(lua_State *L) char portstr[6]; timeout_markstart(tm); count = MIN(count, sizeof(buffer)); - err = socket_recvfrom(&udp->sock, buffer, count, &got, (SA *) &addr, + 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) @@ -247,7 +247,7 @@ static int meth_receivefrom(lua_State *L) lua_pushstring(L, udp_strerror(err)); return 2; } - err = getnameinfo((struct sockaddr *)&addr, addr_len, addrstr, + err = getnameinfo((struct sockaddr *)&addr, addr_len, addrstr, INET6_ADDRSTRLEN, portstr, 6, NI_NUMERICHOST | NI_NUMERICSERV); if (err) { lua_pushnil(L); @@ -351,7 +351,7 @@ static int meth_setpeername(lua_State *L) { /* make sure we try to connect only to the same family */ connecthints.ai_family = udp->family; if (connecting) { - err = inet_tryconnect(&udp->sock, &udp->family, address, + err = inet_tryconnect(&udp->sock, &udp->family, address, port, tm, &connecthints); if (err) { lua_pushnil(L); @@ -365,7 +365,6 @@ static int meth_setpeername(lua_State *L) { inet_trydisconnect(&udp->sock, udp->family, tm); auxiliar_setclass(L, "udp{unconnected}", 1); } - /* change class to connected or unconnected depending on address */ lua_pushnumber(L, 1); return 1; } @@ -410,34 +409,25 @@ static int meth_setsockname(lua_State *L) { * Creates a master udp object \*-------------------------------------------------------------------------*/ static int udp_create(lua_State *L, int family) { - t_socket sock; - /* if family is AF_UNSPEC, we create an AF_INET socket - * but store AF_UNSPEC into tcp-family. This will allow it - * later be replaced with an AF_INET6 socket if - * trybind or tryconnect prefer it instead. */ - const char *err = inet_trycreate(&sock, family == AF_UNSPEC? - AF_INET: family, SOCK_DGRAM); - /* try to allocate a system socket */ - if (!err) { - /* allocate udp object */ - p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp)); - auxiliar_setclass(L, "udp{unconnected}", -1); - /* initialize remaining structure fields */ - socket_setnonblocking(&sock); - if (family == AF_INET6) { - int yes = 1; - setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, - (void *)&yes, sizeof(yes)); + /* allocate udp object */ + p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp)); + auxiliar_setclass(L, "udp{unconnected}", -1); + /* if family is AF_UNSPEC, we leave the socket invalid and + * store AF_UNSPEC into family. This will allow it to later be + * replaced with an AF_INET6 or AF_INET socket upon first use. */ + udp->sock = SOCKET_INVALID; + timeout_init(&udp->tm, -1, -1); + udp->family = family; + if (family != AF_UNSPEC) { + const char *err = inet_trycreate(&udp->sock, family, SOCK_DGRAM, 0); + if (err != NULL) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; } - udp->sock = sock; - timeout_init(&udp->tm, -1, -1); - udp->family = family; - return 1; - } else { - lua_pushnil(L); - lua_pushstring(L, err); - return 2; + socket_setnonblocking(&udp->sock); } + return 1; } static int global_create(lua_State *L) { diff --git a/test/testclnt.lua b/test/testclnt.lua index abf9608..ee1201f 100644 --- a/test/testclnt.lua +++ b/test/testclnt.lua @@ -304,15 +304,20 @@ function isclosed(c) end function active_close() - reconnect() - if isclosed(data) then fail("should not be closed") end - data:close() - if not isclosed(data) then fail("should be closed") end - data = nil - local udp = socket.udp() + local tcp = socket.tcp4() + if isclosed(tcp) then fail("should not be closed") end + tcp:close() + if not isclosed(tcp) then fail("should be closed") end + tcp = socket.tcp() + if not isclosed(tcp) then fail("should be closed") end + tcp = nil + local udp = socket.udp4() if isclosed(udp) then fail("should not be closed") end udp:close() if not isclosed(udp) then fail("should be closed") end + udp = socket.udp() + if not isclosed(udp) then fail("should be closed") end + udp = nil pass("ok") end @@ -368,7 +373,7 @@ function test_selectbugs() pass("invalid input: ok") local toomany = {} for i = 1, socket._SETSIZE+1 do - toomany[#toomany+1] = socket.udp() + toomany[#toomany+1] = socket.udp4() end if #toomany > socket._SETSIZE then local e = pcall(socket.select, toomany, nil, 0.1) -- cgit v1.2.3-55-g6feb