mirror of
https://github.com/lunarmodules/luasocket.git
synced 2024-11-08 14:28:21 +01:00
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".
This commit is contained in:
parent
03ba06f70c
commit
6368caeb5a
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
*.o
|
||||
*.so
|
||||
*.so.*
|
||||
mac
|
||||
|
2
TODO
2
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
|
||||
|
@ -125,7 +125,9 @@ local host.
|
||||
<tt>Port</tt> must be an integer number in the range [0..64K).
|
||||
If <tt>address</tt>
|
||||
is '<tt>*</tt>', the system binds to all local interfaces
|
||||
using the <tt>INADDR_ANY</tt> constant. If <tt>port</tt> is 0, the system automatically
|
||||
using the <tt>INADDR_ANY</tt> constant or
|
||||
<tt>IN6ADDR_ANY_INIT</tt>, according to the family.
|
||||
If <tt>port</tt> is 0, the system automatically
|
||||
chooses an ephemeral port.
|
||||
</p>
|
||||
|
||||
|
45
src/inet.c
45
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;
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
35
src/udp.c
35
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;
|
||||
}
|
||||
|
19
test/udpconnectclnt.lua
Normal file
19
test/udpconnectclnt.lua
Normal file
@ -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)
|
16
test/udpconnectsrvr.lua
Normal file
16
test/udpconnectsrvr.lua
Normal file
@ -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()
|
Loading…
Reference in New Issue
Block a user