From 6368caeb5ab5f628b8021c8ebf4d6df436162aaf Mon Sep 17 00:00:00 2001 From: Diego Nehab Date: Thu, 23 Aug 2012 19:31:15 -0300 Subject: [PATCH] Fix udp:setpeername("*") There seems to be a curious difference between MacOS and Linux and I am not sure if this is documented. When you break a "connection" on Mac OS, you only eliminate the peer association, but the local address remains bound. On Linux, breaking a "connection" eliminates the binding to the local address. Have you guys ever come accross this? Another irritating difference is that connect() returns the error EAFNOSUPPORT on Mac OS. I am going to ignore all errors when the reason for calling connect() is simply to break the "connection". --- .gitignore | 1 + TODO | 2 ++ doc/tcp.html | 4 +++- src/inet.c | 45 ++++++++++++++++++++++++++++++----------- src/inet.h | 1 + src/tcp.c | 4 ++-- src/udp.c | 35 +++++++++++++++++--------------- test/udpconnectclnt.lua | 19 +++++++++++++++++ test/udpconnectsrvr.lua | 16 +++++++++++++++ 9 files changed, 96 insertions(+), 31 deletions(-) create mode 100644 test/udpconnectclnt.lua create mode 100644 test/udpconnectsrvr.lua diff --git a/.gitignore b/.gitignore index 705ce5b..0050a8a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.o *.so *.so.* +mac diff --git a/TODO b/TODO index cc49daa..886fd1e 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,5 @@ +- bizarre default values for getnameinfo should throw error instead! + - document the new bind and connect behavior. - shouldn't we instead make the code compatible to Lua 5.2 without any compat stuff, and use a compatibility layer to diff --git a/doc/tcp.html b/doc/tcp.html index 5f39d0e..151a4c9 100644 --- a/doc/tcp.html +++ b/doc/tcp.html @@ -125,7 +125,9 @@ local host. Port must be an integer number in the range [0..64K). If address is '*', the system binds to all local interfaces -using the INADDR_ANY constant. If port is 0, the system automatically +using the INADDR_ANY constant or +IN6ADDR_ANY_INIT, according to the family. +If port is 0, the system automatically chooses an ephemeral port.

diff --git a/src/inet.c b/src/inet.c index e769cd8..dfee700 100644 --- a/src/inet.c +++ b/src/inet.c @@ -177,8 +177,8 @@ static int inet_global_getaddrinfo(lua_State *L) lua_newtable(L); for (iterator = resolved; iterator; iterator = iterator->ai_next) { char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; - getnameinfo(iterator->ai_addr, iterator->ai_addrlen, hbuf, sizeof(hbuf), - sbuf, 0, NI_NUMERICHOST); + getnameinfo(iterator->ai_addr, iterator->ai_addrlen, hbuf, + sizeof(hbuf), sbuf, 0, NI_NUMERICHOST); lua_pushnumber(L, i); lua_newtable(L); switch (iterator->ai_family) { @@ -367,6 +367,34 @@ const char *inet_trycreate(p_socket ps, int family, int type) { return socket_strerror(socket_create(ps, family, type, 0)); } +/*-------------------------------------------------------------------------*\ +* "Disconnects" a DGRAM socket +\*-------------------------------------------------------------------------*/ +const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm) +{ + switch (family) { + case PF_INET: { + struct sockaddr_in sin; + memset((char *) &sin, 0, sizeof(sin)); + sin.sin_family = AF_UNSPEC; + sin.sin_addr.s_addr = INADDR_ANY; + return socket_strerror(socket_connect(ps, (SA *) &sin, + sizeof(sin), tm)); + } + case PF_INET6: { + struct sockaddr_in6 sin6; + struct in6_addr addrany = IN6ADDR_ANY_INIT; + memset((char *) &sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_UNSPEC; +fprintf(stderr, "disconnecting\n"); + sin6.sin6_addr = addrany; + return socket_strerror(socket_connect(ps, (SA *) &sin6, + sizeof(sin6), tm)); + } + } + return NULL; +} + /*-------------------------------------------------------------------------*\ * Tries to connect to remote address (address, port) \*-------------------------------------------------------------------------*/ @@ -382,17 +410,14 @@ const char *inet_tryconnect(p_socket ps, const char *address, if (resolved) freeaddrinfo(resolved); return err; } - /* iterate over all returned addresses trying to connect */ for (iterator = resolved; iterator; iterator = iterator->ai_next) { timeout_markstart(tm); /* try connecting to remote address */ - err = socket_strerror(socket_connect(ps, - (SA *) iterator->ai_addr, + err = socket_strerror(socket_connect(ps, (SA *) iterator->ai_addr, iterator->ai_addrlen, tm)); /* if success, break out of loop */ if (err == NULL) break; } - freeaddrinfo(resolved); /* here, if err is set, we failed */ return err; @@ -407,12 +432,8 @@ const char *inet_trybind(p_socket ps, const char *address, const char *serv, struct addrinfo *iterator = NULL, *resolved = NULL; const char *err = NULL; t_socket sock = *ps; - /* translate luasocket special values to C */ - if (strcmp(address, "*") == 0) address = NULL; - if (!serv) serv = "0"; /* try resolving */ - err = socket_gaistrerror(getaddrinfo(address, serv, - bindhints, &resolved)); + err = socket_gaistrerror(getaddrinfo(address, serv, bindhints, &resolved)); if (err) { if (resolved) freeaddrinfo(resolved); return err; @@ -420,7 +441,7 @@ const char *inet_trybind(p_socket ps, const char *address, const char *serv, /* iterate over resolved addresses until one is good */ for (iterator = resolved; iterator; iterator = iterator->ai_next) { if(sock == SOCKET_INVALID) { - err = socket_strerror( socket_create(&sock, iterator->ai_family, + err = socket_strerror(socket_create(&sock, iterator->ai_family, iterator->ai_socktype, iterator->ai_protocol)); if(err) continue; diff --git a/src/inet.h b/src/inet.h index 05633bb..2346734 100644 --- a/src/inet.h +++ b/src/inet.h @@ -29,6 +29,7 @@ const char *inet_tryconnect(p_socket ps, const char *address, const char *serv, p_timeout tm, struct addrinfo *connecthints); const char *inet_trybind(p_socket ps, const char *address, const char *serv, struct addrinfo *bindhints); +const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm); int inet_meth_getpeername(lua_State *L, p_socket ps, int family); int inet_meth_getsockname(lua_State *L, p_socket ps, int family); diff --git a/src/tcp.c b/src/tcp.c index 2085937..5c85ae0 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -222,6 +222,7 @@ static int meth_bind(lua_State *L) bindhints.ai_socktype = SOCK_STREAM; bindhints.ai_family = tcp->family; bindhints.ai_flags = AI_PASSIVE; + address = strcmp(address, "*")? address: NULL; err = inet_trybind(&tcp->sock, address, port, &bindhints); if (err) { lua_pushnil(L); @@ -247,8 +248,7 @@ static int meth_connect(lua_State *L) /* make sure we try to connect only to the same family */ connecthints.ai_family = tcp->family; timeout_markstart(&tcp->tm); - err = inet_tryconnect(&tcp->sock, address, port, - &tcp->tm, &connecthints); + err = inet_tryconnect(&tcp->sock, address, port, &tcp->tm, &connecthints); /* have to set the class even if it failed due to non-blocking connects */ auxiliar_setclass(L, "tcp{client}", 1); if (err) { diff --git a/src/udp.c b/src/udp.c index bdf584b..4cd9a41 100644 --- a/src/udp.c +++ b/src/udp.c @@ -275,10 +275,10 @@ static int meth_receivefrom(lua_State *L) { } break; } - default: - lua_pushnil(L); - lua_pushfstring(L, "unknown family %d", udp->family); - return 2; + default: + lua_pushnil(L); + lua_pushfstring(L, "unknown family %d", udp->family); + return 2; } lua_pushnil(L); lua_pushstring(L, udp_strerror(err)); @@ -366,27 +366,30 @@ static int meth_settimeout(lua_State *L) { static int meth_setpeername(lua_State *L) { p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); p_timeout tm = &udp->tm; - const char *address = luaL_checkstring(L, 2); + const char *address = luaL_checkstring(L, 2); int connecting = strcmp(address, "*"); - const char *port = connecting ? - luaL_checkstring(L, 3) : - luaL_optstring(L, 3, "0"); + const char *port = connecting? luaL_checkstring(L, 3): "0"; struct addrinfo connecthints; const char *err; memset(&connecthints, 0, sizeof(connecthints)); connecthints.ai_socktype = SOCK_DGRAM; /* make sure we try to connect only to the same family */ connecthints.ai_family = udp->family; - err = inet_tryconnect(&udp->sock, address, port, - tm, &connecthints); - if (err) { - lua_pushnil(L); - lua_pushstring(L, err); - return 2; + if (connecting) { + err = inet_tryconnect(&udp->sock, address, port, tm, &connecthints); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + auxiliar_setclass(L, "udp{connected}", 1); + } else { + /* we ignore possible errors because Mac OS X always + * returns EAFNOSUPPORT */ + inet_trydisconnect(&udp->sock, udp->family, tm); + auxiliar_setclass(L, "udp{unconnected}", 1); } /* change class to connected or unconnected depending on address */ - if (connecting) auxiliar_setclass(L, "udp{connected}", 1); - else auxiliar_setclass(L, "udp{unconnected}", 1); lua_pushnumber(L, 1); return 1; } diff --git a/test/udpconnectclnt.lua b/test/udpconnectclnt.lua new file mode 100644 index 0000000..effe13a --- /dev/null +++ b/test/udpconnectclnt.lua @@ -0,0 +1,19 @@ +local socket = require"socket" +local udp = socket.udp +local localhost = "127.0.0.1" +local port = arg[1] + +se = udp(); se:setoption("reuseaddr", true) +se:setsockname(localhost, 5062) +print("se", se:getsockname()) +sc = udp(); sc:setoption("reuseaddr", true) +sc:setsockname(localhost, 5061) +print("sc", sc:getsockname()) + +se:sendto("this is a test from se", localhost, port) +socket.sleep(1) +sc:sendto("this is a test from sc", localhost, port) +socket.sleep(1) +se:sendto("this is a test from se", localhost, port) +socket.sleep(1) +sc:sendto("this is a test from sc", localhost, port) diff --git a/test/udpconnectsrvr.lua b/test/udpconnectsrvr.lua new file mode 100644 index 0000000..5a9772e --- /dev/null +++ b/test/udpconnectsrvr.lua @@ -0,0 +1,16 @@ +local socket = require"socket" +local udp = socket.udp +local localhost = "127.0.0.1" +local s = assert(udp()) +assert(tostring(s):find("udp{unconnected}")) +print("setpeername", s:setpeername(localhost, 5061)) +print("getsockname", s:getsockname()) +assert(tostring(s):find("udp{connected}")) +print(s:receive()) +print("setpeername", s:setpeername("*")) +print("getsockname", s:getsockname()) +s:sendto("a", localhost, 12345) +print("getsockname", s:getsockname()) +assert(tostring(s):find("udp{unconnected}")) +print(s:receivefrom()) +s:close()