Fixed inet_pton and a new Winsock UDP bug.
inet_pton was copying the entire sockaddr_in struct, rather than just the sin_addr field... I am a bit unsure about the UDP fix, because it may affect TCP as well. On UDP sockets, when a sendto fails, the next receive/receivefrom fails with CONNRESET. I changed sock_recv/sock_recvfrom in wsocket.c to skip the CONNRESET from the recv/recvfrom, hoping that if the socket is TCP, sock_waitfd will get the CONNRESET again. The tests pass, but this should be tested more thoroughly.
This commit is contained in:
parent
66cd8cfcee
commit
734cc23e1f
17
src/inet.c
17
src/inet.c
@ -558,18 +558,23 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
|
|||||||
|
|
||||||
int inet_pton(int af, const char *src, void *dst)
|
int inet_pton(int af, const char *src, void *dst)
|
||||||
{
|
{
|
||||||
struct addrinfo hints, *res, *ressave;
|
struct addrinfo hints, *res;
|
||||||
memset(&hints, 0, sizeof(struct addrinfo));
|
memset(&hints, 0, sizeof(struct addrinfo));
|
||||||
hints.ai_family = af;
|
hints.ai_family = af;
|
||||||
|
hints.ai_flags = AI_NUMERICHOST;
|
||||||
if (getaddrinfo(src, NULL, &hints, &res) != 0) {
|
if (getaddrinfo(src, NULL, &hints, &res) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
ressave = res;
|
if (af == AF_INET) {
|
||||||
while (res) {
|
struct sockaddr_in *in = (struct sockaddr_in *) res->ai_addr;
|
||||||
memcpy(dst, res->ai_addr, res->ai_addrlen);
|
memcpy(dst, &in->sin_addr, sizeof(in->sin_addr));
|
||||||
res = res->ai_next;
|
} else if (af == AF_INET6) {
|
||||||
|
struct sockaddr_in6 *in = (struct sockaddr_in6 *) res->ai_addr;
|
||||||
|
memcpy(dst, &in->sin6_addr, sizeof(in->sin6_addr));
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
freeaddrinfo(ressave);
|
freeaddrinfo(res);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
2
src/io.h
2
src/io.h
@ -22,7 +22,7 @@ enum {
|
|||||||
IO_DONE = 0, /* operation completed successfully */
|
IO_DONE = 0, /* operation completed successfully */
|
||||||
IO_TIMEOUT = -1, /* operation timed out */
|
IO_TIMEOUT = -1, /* operation timed out */
|
||||||
IO_CLOSED = -2, /* the connection has been closed */
|
IO_CLOSED = -2, /* the connection has been closed */
|
||||||
IO_UNKNOWN = -3
|
IO_UNKNOWN = -3
|
||||||
};
|
};
|
||||||
|
|
||||||
/* interface to error message function */
|
/* interface to error message function */
|
||||||
|
122
src/udp.c
122
src/udp.c
@ -155,31 +155,31 @@ static int meth_sendto(lua_State *L) {
|
|||||||
p_timeout tm = &udp->tm;
|
p_timeout tm = &udp->tm;
|
||||||
int err;
|
int err;
|
||||||
switch (udp->family) {
|
switch (udp->family) {
|
||||||
case PF_INET: {
|
case PF_INET: {
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
memset(&addr, 0, sizeof(addr));
|
memset(&addr, 0, sizeof(addr));
|
||||||
if (inet_pton(AF_INET, ip, &addr.sin_addr) != 1)
|
if (inet_pton(AF_INET, ip, &addr.sin_addr) != 1)
|
||||||
luaL_argerror(L, 3, "invalid ip address");
|
luaL_argerror(L, 3, "invalid ip address");
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
addr.sin_port = htons(port);
|
addr.sin_port = htons(port);
|
||||||
timeout_markstart(tm);
|
timeout_markstart(tm);
|
||||||
err = socket_sendto(&udp->sock, data, count, &sent,
|
err = socket_sendto(&udp->sock, data, count, &sent,
|
||||||
(SA *) &addr, sizeof(addr), tm);
|
(SA *) &addr, sizeof(addr), tm);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PF_INET6: {
|
case PF_INET6: {
|
||||||
struct sockaddr_in6 addr;
|
struct sockaddr_in6 addr;
|
||||||
memset(&addr, 0, sizeof(addr));
|
memset(&addr, 0, sizeof(addr));
|
||||||
if (!inet_pton(AF_INET6, ip, &addr.sin6_addr) != 1)
|
if (!inet_pton(AF_INET6, ip, &addr.sin6_addr) != 1)
|
||||||
luaL_argerror(L, 3, "invalid ip address");
|
luaL_argerror(L, 3, "invalid ip address");
|
||||||
addr.sin6_family = AF_INET6;
|
addr.sin6_family = AF_INET6;
|
||||||
addr.sin6_port = htons(port);
|
addr.sin6_port = htons(port);
|
||||||
timeout_markstart(tm);
|
timeout_markstart(tm);
|
||||||
err = socket_sendto(&udp->sock, data, count, &sent,
|
err = socket_sendto(&udp->sock, data, count, &sent,
|
||||||
(SA *) &addr, sizeof(addr), tm);
|
(SA *) &addr, sizeof(addr), tm);
|
||||||
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;
|
||||||
@ -229,38 +229,40 @@ static int meth_receivefrom(lua_State *L) {
|
|||||||
timeout_markstart(tm);
|
timeout_markstart(tm);
|
||||||
count = MIN(count, sizeof(buffer));
|
count = MIN(count, sizeof(buffer));
|
||||||
switch (udp->family) {
|
switch (udp->family) {
|
||||||
case PF_INET: {
|
case PF_INET: {
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
socklen_t addr_len = sizeof(addr);
|
socklen_t addr_len = sizeof(addr);
|
||||||
err = socket_recvfrom(&udp->sock, buffer, count, &got,
|
err = socket_recvfrom(&udp->sock, buffer, count, &got,
|
||||||
(SA *) &addr, &addr_len, tm);
|
(SA *) &addr, &addr_len, tm);
|
||||||
/* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */
|
/* Unlike TCP, recv() of zero is not closed,
|
||||||
if (err == IO_CLOSED)
|
* but a zero-length packet. */
|
||||||
err = IO_DONE;
|
if (err == IO_CLOSED)
|
||||||
if (err == IO_DONE) {
|
|
||||||
char addrstr[INET_ADDRSTRLEN];
|
|
||||||
lua_pushlstring(L, buffer, got);
|
|
||||||
if (!inet_ntop(AF_INET, &addr.sin_addr,
|
|
||||||
addrstr, sizeof(addrstr))) {
|
|
||||||
lua_pushnil(L);
|
|
||||||
lua_pushstring(L, "invalid source address");
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
lua_pushstring(L, addrstr);
|
|
||||||
lua_pushnumber(L, ntohs(addr.sin_port));
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PF_INET6: {
|
|
||||||
struct sockaddr_in6 addr;
|
|
||||||
socklen_t addr_len = sizeof(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)
|
|
||||||
err = IO_DONE;
|
err = IO_DONE;
|
||||||
if (err == IO_DONE) {
|
if (err == IO_DONE) {
|
||||||
|
char addrstr[INET_ADDRSTRLEN];
|
||||||
|
lua_pushlstring(L, buffer, got);
|
||||||
|
if (!inet_ntop(AF_INET, &addr.sin_addr,
|
||||||
|
addrstr, sizeof(addrstr))) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, "invalid source address");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushstring(L, addrstr);
|
||||||
|
lua_pushnumber(L, ntohs(addr.sin_port));
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PF_INET6: {
|
||||||
|
struct sockaddr_in6 addr;
|
||||||
|
socklen_t addr_len = sizeof(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)
|
||||||
|
err = IO_DONE;
|
||||||
|
if (err == IO_DONE) {
|
||||||
char addrstr[INET6_ADDRSTRLEN];
|
char addrstr[INET6_ADDRSTRLEN];
|
||||||
lua_pushlstring(L, buffer, got);
|
lua_pushlstring(L, buffer, got);
|
||||||
if (!inet_ntop(AF_INET6, &addr.sin6_addr,
|
if (!inet_ntop(AF_INET6, &addr.sin6_addr,
|
||||||
@ -272,9 +274,9 @@ static int meth_receivefrom(lua_State *L) {
|
|||||||
lua_pushstring(L, addrstr);
|
lua_pushstring(L, addrstr);
|
||||||
lua_pushnumber(L, ntohs(addr.sin6_port));
|
lua_pushnumber(L, ntohs(addr.sin6_port));
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
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);
|
||||||
@ -413,7 +415,7 @@ static int meth_setsockname(lua_State *L) {
|
|||||||
const char *address = luaL_checkstring(L, 2);
|
const char *address = luaL_checkstring(L, 2);
|
||||||
const char *port = luaL_checkstring(L, 3);
|
const char *port = luaL_checkstring(L, 3);
|
||||||
const char *err;
|
const char *err;
|
||||||
struct addrinfo bindhints;
|
struct addrinfo bindhints;
|
||||||
memset(&bindhints, 0, sizeof(bindhints));
|
memset(&bindhints, 0, sizeof(bindhints));
|
||||||
bindhints.ai_socktype = SOCK_DGRAM;
|
bindhints.ai_socktype = SOCK_DGRAM;
|
||||||
bindhints.ai_family = udp->family;
|
bindhints.ai_family = udp->family;
|
||||||
@ -461,9 +463,9 @@ static int udp_create(lua_State *L, int family) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int global_create(lua_State *L) {
|
static int global_create(lua_State *L) {
|
||||||
return udp_create(L, AF_INET);
|
return udp_create(L, AF_INET);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int global_create6(lua_State *L) {
|
static int global_create6(lua_State *L) {
|
||||||
return udp_create(L, AF_INET6);
|
return udp_create(L, AF_INET6);
|
||||||
}
|
}
|
||||||
|
@ -250,7 +250,11 @@ int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm
|
|||||||
}
|
}
|
||||||
if (taken == 0) return IO_CLOSED;
|
if (taken == 0) return IO_CLOSED;
|
||||||
err = WSAGetLastError();
|
err = WSAGetLastError();
|
||||||
if (err != WSAEWOULDBLOCK) return err;
|
/* On Windows, and on UDP, a connreset simply means the
|
||||||
|
* previous send failed. On TCP, it means our socket
|
||||||
|
* is now useless, so the error must pass. I am
|
||||||
|
* hoping waitfd will still get the error. */
|
||||||
|
if (err != WSAEWOULDBLOCK && err != WSAECONNRESET) return err;
|
||||||
if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
|
if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -271,7 +275,11 @@ int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got,
|
|||||||
}
|
}
|
||||||
if (taken == 0) return IO_CLOSED;
|
if (taken == 0) return IO_CLOSED;
|
||||||
err = WSAGetLastError();
|
err = WSAGetLastError();
|
||||||
if (err != WSAEWOULDBLOCK) return err;
|
/* On Windows, and on UDP, a connreset simply means the
|
||||||
|
* previous send failed. On TCP, it means our socket
|
||||||
|
* is now useless, so the error must pass. I am
|
||||||
|
* hoping waitfd will still get the error. */
|
||||||
|
if (err != WSAEWOULDBLOCK && err != WSAECONNRESET) return err;
|
||||||
if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
|
if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user