# HG changeset patch # User jbe # Date 1517071695 -3600 # Node ID 930a43a0099a76102b288da043de6feec98131a4 # Parent 426848ae610825668af0c1f29766ce7c47547654 Bugfix/workaround: Do not call getpeername() when opening TCP connections in non-blocking mode diff -r 426848ae6108 -r 930a43a0099a moonbridge_io.c --- a/moonbridge_io.c Fri Nov 24 01:32:23 2017 +0100 +++ b/moonbridge_io.c Sat Jan 27 17:48:15 2018 +0100 @@ -873,10 +873,12 @@ static int moonbr_io_pushhandle_impl(lua_State *L) { int *fd; + int skip_peeraddr; moonbr_io_handle_t *handle; struct sockaddr addr; socklen_t addrlen; fd = lua_touserdata(L, 1); + skip_peeraddr = lua_toboolean(L, 2); handle = lua_newuserdata(L, sizeof(moonbr_io_handle_t)); handle->fd = -1; /* avoid closing incomplete handle */ addrlen = sizeof(addr); @@ -928,6 +930,8 @@ char addrstrbuf[INET6_ADDRSTRLEN]; const char *addrstr; addrlen = sizeof(addr_in6); + /* NOTE: According to documentation, getsockname() may fail if connection + * was reset. There seems to be no problem in practice though. */ if (getsockname(*fd, (struct sockaddr *)&addr_in6, &addrlen)) { moonbr_io_prepare_errmsg(); luaL_error(L, "Could not determine local IP address/port: %s", errmsg); @@ -945,28 +949,34 @@ } lua_pushinteger(L, ntohs(addr_in6.sin6_port)); lua_setfield(L, -2, "local_tcpport"); - if (getpeername(*fd, (struct sockaddr *)&addr_in6, &addrlen)) { - moonbr_io_prepare_errmsg(); - luaL_error(L, "Could not determine remote IP address/port: %s", errmsg); - } - if (addrlen > sizeof(addr_in6)) { - luaL_error(L, "Could not determine remote IP address/port: buffer size exceeded"); + if (!skip_peeraddr) { + /* NOTE: According to documentation, getpeername() may fail if connection + * was reset. There seems to be no problem in practice though. */ + if (getpeername(*fd, (struct sockaddr *)&addr_in6, &addrlen)) { + moonbr_io_prepare_errmsg(); + luaL_error(L, "Could not determine remote IP address/port: %s", errmsg); + } + if (addrlen > sizeof(addr_in6)) { + luaL_error(L, "Could not determine remote IP address/port: buffer size exceeded"); + } + addrstr = inet_ntop(AF_INET6, addr_in6.sin6_addr.s6_addr, addrstrbuf, sizeof(addrstrbuf)); + if (!addrstr) { + moonbr_io_prepare_errmsg(); + luaL_error(L, "Could not format remote IP address: %s", errmsg); + } else { + lua_pushstring(L, addrstr); + lua_setfield(L, -2, "remote_ip6"); + } + lua_pushinteger(L, ntohs(addr_in6.sin6_port)); + lua_setfield(L, -2, "remote_tcpport"); } - addrstr = inet_ntop(AF_INET6, addr_in6.sin6_addr.s6_addr, addrstrbuf, sizeof(addrstrbuf)); - if (!addrstr) { - moonbr_io_prepare_errmsg(); - luaL_error(L, "Could not format remote IP address: %s", errmsg); - } else { - lua_pushstring(L, addrstr); - lua_setfield(L, -2, "remote_ip6"); - } - lua_pushinteger(L, ntohs(addr_in6.sin6_port)); - lua_setfield(L, -2, "remote_tcpport"); } else if (handle->addrfam == AF_INET) { struct sockaddr_in addr_in; char addrstrbuf[INET_ADDRSTRLEN]; const char *addrstr; addrlen = sizeof(addr_in); + /* NOTE: According to documentation, getsockname() may fail if connection + * was reset. There seems to be no problem in practice though. */ if (getsockname(*fd, (struct sockaddr *)&addr_in, &addrlen)) { moonbr_io_prepare_errmsg(); luaL_error(L, "Could not determine local IP address/port: %s", errmsg); @@ -984,23 +994,27 @@ } lua_pushinteger(L, ntohs(addr_in.sin_port)); lua_setfield(L, -2, "local_tcpport"); - if (getpeername(*fd, (struct sockaddr *)&addr_in, &addrlen)) { - moonbr_io_prepare_errmsg(); - luaL_error(L, "Could not determine remote IP address/port: %s", errmsg); - } - if (addrlen > sizeof(addr_in)) { - luaL_error(L, "Could not determine remote IP address/port: buffer size exceeded"); + if (!skip_peeraddr) { + /* NOTE: According to documentation, getpeername() may fail if connection + * was reset. There seems to be no problem in practice though. */ + if (getpeername(*fd, (struct sockaddr *)&addr_in, &addrlen)) { + moonbr_io_prepare_errmsg(); + luaL_error(L, "Could not determine remote IP address/port: %s", errmsg); + } + if (addrlen > sizeof(addr_in)) { + luaL_error(L, "Could not determine remote IP address/port: buffer size exceeded"); + } + addrstr = inet_ntop(AF_INET, &addr_in.sin_addr.s_addr, addrstrbuf, sizeof(addrstrbuf)); + if (!addrstr) { + moonbr_io_prepare_errmsg(); + luaL_error(L, "Could not format remote IP address: %s", errmsg); + } else { + lua_pushstring(L, addrstr); + lua_setfield(L, -2, "remote_ip4"); + } + lua_pushinteger(L, ntohs(addr_in.sin_port)); + lua_setfield(L, -2, "remote_tcpport"); } - addrstr = inet_ntop(AF_INET, &addr_in.sin_addr.s_addr, addrstrbuf, sizeof(addrstrbuf)); - if (!addrstr) { - moonbr_io_prepare_errmsg(); - luaL_error(L, "Could not format remote IP address: %s", errmsg); - } else { - lua_pushstring(L, addrstr); - lua_setfield(L, -2, "remote_ip4"); - } - lua_pushinteger(L, ntohs(addr_in.sin_port)); - lua_setfield(L, -2, "remote_tcpport"); } luaL_setmetatable(L, MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY); lua_setfield(L, -2, "public"); @@ -1019,6 +1033,16 @@ } } +void moonbr_io_pushhandle_skip_peeraddr(lua_State *L, int fd) { + lua_pushcfunction(L, moonbr_io_pushhandle_impl); + lua_pushlightuserdata(L, &fd); + lua_pushboolean(L, 1); + if (lua_pcall(L, 2, 1, 0)) { + if (fd != -1) close(fd); // TODO: correct to close file descriptor here? + lua_error(L); + } +} + static int moonbr_io_handleindex(lua_State *L) { luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); luaL_checkany(L, 2); @@ -1129,7 +1153,16 @@ } else { freeaddrinfo(res); } - moonbr_io_pushhandle(L, sock); + if (nonblocking) { + moonbr_io_pushhandle_skip_peeraddr(L, sock); + if (addrinfo->ai_family == AF_INET6) { + // TODO: fill remote_ip6 and remote_tcpport + } else if (addrinfo->ai_family == AF_INET) { + // TODO: fill remote_ip4 and remote_tcpport + } + } else { + moonbr_io_pushhandle(L, sock); + } return 1; }