mirror of
https://github.com/lunarmodules/luasocket.git
synced 2024-11-16 02:08: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
|
*.o
|
||||||
*.so
|
*.so
|
||||||
*.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.
|
- document the new bind and connect behavior.
|
||||||
- shouldn't we instead make the code compatible to Lua 5.2
|
- shouldn't we instead make the code compatible to Lua 5.2
|
||||||
without any compat stuff, and use a compatibility layer to
|
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).
|
<tt>Port</tt> must be an integer number in the range [0..64K).
|
||||||
If <tt>address</tt>
|
If <tt>address</tt>
|
||||||
is '<tt>*</tt>', the system binds to all local interfaces
|
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.
|
chooses an ephemeral port.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
45
src/inet.c
45
src/inet.c
@ -177,8 +177,8 @@ static int inet_global_getaddrinfo(lua_State *L)
|
|||||||
lua_newtable(L);
|
lua_newtable(L);
|
||||||
for (iterator = resolved; iterator; iterator = iterator->ai_next) {
|
for (iterator = resolved; iterator; iterator = iterator->ai_next) {
|
||||||
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
|
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
|
||||||
getnameinfo(iterator->ai_addr, iterator->ai_addrlen, hbuf, sizeof(hbuf),
|
getnameinfo(iterator->ai_addr, iterator->ai_addrlen, hbuf,
|
||||||
sbuf, 0, NI_NUMERICHOST);
|
sizeof(hbuf), sbuf, 0, NI_NUMERICHOST);
|
||||||
lua_pushnumber(L, i);
|
lua_pushnumber(L, i);
|
||||||
lua_newtable(L);
|
lua_newtable(L);
|
||||||
switch (iterator->ai_family) {
|
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));
|
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)
|
* 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);
|
if (resolved) freeaddrinfo(resolved);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
/* iterate over all returned addresses trying to connect */
|
|
||||||
for (iterator = resolved; iterator; iterator = iterator->ai_next) {
|
for (iterator = resolved; iterator; iterator = iterator->ai_next) {
|
||||||
timeout_markstart(tm);
|
timeout_markstart(tm);
|
||||||
/* try connecting to remote address */
|
/* try connecting to remote address */
|
||||||
err = socket_strerror(socket_connect(ps,
|
err = socket_strerror(socket_connect(ps, (SA *) iterator->ai_addr,
|
||||||
(SA *) iterator->ai_addr,
|
|
||||||
iterator->ai_addrlen, tm));
|
iterator->ai_addrlen, tm));
|
||||||
/* if success, break out of loop */
|
/* if success, break out of loop */
|
||||||
if (err == NULL) break;
|
if (err == NULL) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
freeaddrinfo(resolved);
|
freeaddrinfo(resolved);
|
||||||
/* here, if err is set, we failed */
|
/* here, if err is set, we failed */
|
||||||
return err;
|
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;
|
struct addrinfo *iterator = NULL, *resolved = NULL;
|
||||||
const char *err = NULL;
|
const char *err = NULL;
|
||||||
t_socket sock = *ps;
|
t_socket sock = *ps;
|
||||||
/* translate luasocket special values to C */
|
|
||||||
if (strcmp(address, "*") == 0) address = NULL;
|
|
||||||
if (!serv) serv = "0";
|
|
||||||
/* try resolving */
|
/* try resolving */
|
||||||
err = socket_gaistrerror(getaddrinfo(address, serv,
|
err = socket_gaistrerror(getaddrinfo(address, serv, bindhints, &resolved));
|
||||||
bindhints, &resolved));
|
|
||||||
if (err) {
|
if (err) {
|
||||||
if (resolved) freeaddrinfo(resolved);
|
if (resolved) freeaddrinfo(resolved);
|
||||||
return err;
|
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 */
|
/* iterate over resolved addresses until one is good */
|
||||||
for (iterator = resolved; iterator; iterator = iterator->ai_next) {
|
for (iterator = resolved; iterator; iterator = iterator->ai_next) {
|
||||||
if(sock == SOCKET_INVALID) {
|
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));
|
iterator->ai_socktype, iterator->ai_protocol));
|
||||||
if(err)
|
if(err)
|
||||||
continue;
|
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 *serv, p_timeout tm, struct addrinfo *connecthints);
|
||||||
const char *inet_trybind(p_socket ps, const char *address, const char *serv,
|
const char *inet_trybind(p_socket ps, const char *address, const char *serv,
|
||||||
struct addrinfo *bindhints);
|
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_getpeername(lua_State *L, p_socket ps, int family);
|
||||||
int inet_meth_getsockname(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_socktype = SOCK_STREAM;
|
||||||
bindhints.ai_family = tcp->family;
|
bindhints.ai_family = tcp->family;
|
||||||
bindhints.ai_flags = AI_PASSIVE;
|
bindhints.ai_flags = AI_PASSIVE;
|
||||||
|
address = strcmp(address, "*")? address: NULL;
|
||||||
err = inet_trybind(&tcp->sock, address, port, &bindhints);
|
err = inet_trybind(&tcp->sock, address, port, &bindhints);
|
||||||
if (err) {
|
if (err) {
|
||||||
lua_pushnil(L);
|
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 */
|
/* make sure we try to connect only to the same family */
|
||||||
connecthints.ai_family = tcp->family;
|
connecthints.ai_family = tcp->family;
|
||||||
timeout_markstart(&tcp->tm);
|
timeout_markstart(&tcp->tm);
|
||||||
err = inet_tryconnect(&tcp->sock, address, port,
|
err = inet_tryconnect(&tcp->sock, address, port, &tcp->tm, &connecthints);
|
||||||
&tcp->tm, &connecthints);
|
|
||||||
/* have to set the class even if it failed due to non-blocking connects */
|
/* have to set the class even if it failed due to non-blocking connects */
|
||||||
auxiliar_setclass(L, "tcp{client}", 1);
|
auxiliar_setclass(L, "tcp{client}", 1);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
35
src/udp.c
35
src/udp.c
@ -275,10 +275,10 @@ static int meth_receivefrom(lua_State *L) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
lua_pushfstring(L, "unknown family %d", udp->family);
|
lua_pushfstring(L, "unknown family %d", udp->family);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
lua_pushstring(L, udp_strerror(err));
|
lua_pushstring(L, udp_strerror(err));
|
||||||
@ -366,27 +366,30 @@ static int meth_settimeout(lua_State *L) {
|
|||||||
static int meth_setpeername(lua_State *L) {
|
static int meth_setpeername(lua_State *L) {
|
||||||
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
|
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
|
||||||
p_timeout tm = &udp->tm;
|
p_timeout tm = &udp->tm;
|
||||||
const char *address = luaL_checkstring(L, 2);
|
const char *address = luaL_checkstring(L, 2);
|
||||||
int connecting = strcmp(address, "*");
|
int connecting = strcmp(address, "*");
|
||||||
const char *port = connecting ?
|
const char *port = connecting? luaL_checkstring(L, 3): "0";
|
||||||
luaL_checkstring(L, 3) :
|
|
||||||
luaL_optstring(L, 3, "0");
|
|
||||||
struct addrinfo connecthints;
|
struct addrinfo connecthints;
|
||||||
const char *err;
|
const char *err;
|
||||||
memset(&connecthints, 0, sizeof(connecthints));
|
memset(&connecthints, 0, sizeof(connecthints));
|
||||||
connecthints.ai_socktype = SOCK_DGRAM;
|
connecthints.ai_socktype = SOCK_DGRAM;
|
||||||
/* make sure we try to connect only to the same family */
|
/* make sure we try to connect only to the same family */
|
||||||
connecthints.ai_family = udp->family;
|
connecthints.ai_family = udp->family;
|
||||||
err = inet_tryconnect(&udp->sock, address, port,
|
if (connecting) {
|
||||||
tm, &connecthints);
|
err = inet_tryconnect(&udp->sock, address, port, tm, &connecthints);
|
||||||
if (err) {
|
if (err) {
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
lua_pushstring(L, err);
|
lua_pushstring(L, err);
|
||||||
return 2;
|
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 */
|
/* 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);
|
lua_pushnumber(L, 1);
|
||||||
return 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