From 734cc23e1f03372314ebad07ffd35117c152afcd Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 28 May 2013 00:09:30 +0800 Subject: [PATCH] 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. --- src/inet.c | 17 ++++--- src/io.h | 2 +- src/udp.c | 122 +++++++++++++++++++++++++------------------------- src/wsocket.c | 12 ++++- 4 files changed, 84 insertions(+), 69 deletions(-) diff --git a/src/inet.c b/src/inet.c index fe9769b..1f55d2a 100644 --- a/src/inet.c +++ b/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) { - struct addrinfo hints, *res, *ressave; + struct addrinfo hints, *res; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = af; + hints.ai_flags = AI_NUMERICHOST; if (getaddrinfo(src, NULL, &hints, &res) != 0) { return -1; } - ressave = res; - while (res) { - memcpy(dst, res->ai_addr, res->ai_addrlen); - res = res->ai_next; + if (af == AF_INET) { + struct sockaddr_in *in = (struct sockaddr_in *) res->ai_addr; + memcpy(dst, &in->sin_addr, sizeof(in->sin_addr)); + } 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; } diff --git a/src/io.h b/src/io.h index 8cca08a..76a3e58 100644 --- a/src/io.h +++ b/src/io.h @@ -22,7 +22,7 @@ enum { IO_DONE = 0, /* operation completed successfully */ IO_TIMEOUT = -1, /* operation timed out */ IO_CLOSED = -2, /* the connection has been closed */ - IO_UNKNOWN = -3 + IO_UNKNOWN = -3 }; /* interface to error message function */ diff --git a/src/udp.c b/src/udp.c index 6e74702..3051382 100644 --- a/src/udp.c +++ b/src/udp.c @@ -155,31 +155,31 @@ static int meth_sendto(lua_State *L) { p_timeout tm = &udp->tm; int err; switch (udp->family) { - case PF_INET: { - struct sockaddr_in addr; - memset(&addr, 0, sizeof(addr)); - if (inet_pton(AF_INET, ip, &addr.sin_addr) != 1) + case PF_INET: { + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + if (inet_pton(AF_INET, ip, &addr.sin_addr) != 1) luaL_argerror(L, 3, "invalid ip address"); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - timeout_markstart(tm); - err = socket_sendto(&udp->sock, data, count, &sent, - (SA *) &addr, sizeof(addr), tm); - break; - } - case PF_INET6: { - struct sockaddr_in6 addr; - memset(&addr, 0, sizeof(addr)); - if (!inet_pton(AF_INET6, ip, &addr.sin6_addr) != 1) + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + timeout_markstart(tm); + err = socket_sendto(&udp->sock, data, count, &sent, + (SA *) &addr, sizeof(addr), tm); + break; + } + case PF_INET6: { + struct sockaddr_in6 addr; + memset(&addr, 0, sizeof(addr)); + if (!inet_pton(AF_INET6, ip, &addr.sin6_addr) != 1) luaL_argerror(L, 3, "invalid ip address"); - addr.sin6_family = AF_INET6; - addr.sin6_port = htons(port); - timeout_markstart(tm); - err = socket_sendto(&udp->sock, data, count, &sent, - (SA *) &addr, sizeof(addr), tm); - break; - } - default: + addr.sin6_family = AF_INET6; + addr.sin6_port = htons(port); + timeout_markstart(tm); + err = socket_sendto(&udp->sock, data, count, &sent, + (SA *) &addr, sizeof(addr), tm); + break; + } + default: lua_pushnil(L); lua_pushfstring(L, "unknown family %d", udp->family); return 2; @@ -229,38 +229,40 @@ static int meth_receivefrom(lua_State *L) { timeout_markstart(tm); count = MIN(count, sizeof(buffer)); switch (udp->family) { - case PF_INET: { - struct sockaddr_in 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[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) + case PF_INET: { + struct sockaddr_in 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) { + 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]; lua_pushlstring(L, buffer, got); if (!inet_ntop(AF_INET6, &addr.sin6_addr, @@ -272,9 +274,9 @@ static int meth_receivefrom(lua_State *L) { lua_pushstring(L, addrstr); lua_pushnumber(L, ntohs(addr.sin6_port)); return 3; - } - break; - } + } + break; + } default: lua_pushnil(L); 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 *port = luaL_checkstring(L, 3); const char *err; - struct addrinfo bindhints; + struct addrinfo bindhints; memset(&bindhints, 0, sizeof(bindhints)); bindhints.ai_socktype = SOCK_DGRAM; 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) { - return udp_create(L, AF_INET); + return udp_create(L, AF_INET); } static int global_create6(lua_State *L) { - return udp_create(L, AF_INET6); + return udp_create(L, AF_INET6); } diff --git a/src/wsocket.c b/src/wsocket.c index 65f76bc..d34724b 100644 --- a/src/wsocket.c +++ b/src/wsocket.c @@ -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; 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; } } @@ -271,7 +275,11 @@ int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, } if (taken == 0) return IO_CLOSED; 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; } }