From 91716a8661679b0f96aed4e22bb511b14d148690 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 28 Dec 2018 05:27:59 +0000 Subject: [PATCH 01/11] httpserver: Clone evhttp_bind_socket_with_handle locally --- src/httpserver.cpp | 126 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 687a5550ea..9dc274e98b 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -356,6 +356,130 @@ static void ThreadHTTP(struct event_base* base) LogPrint(BCLog::HTTP, "Exited http event loop\n"); } +// Temporary hacks to avoid modifying libevent code yet +#define event_debug(x) LogPrintf x +#define event_warn LogPrintf +#define event_warnx LogPrintf +#define event_sock_warn(sock, ...) LogPrintf(__VA_ARGS__) + +/* Create a non-blocking socket and bind it */ +/* todo: rename this function */ +static evutil_socket_t +bind_socket_ai(struct evutil_addrinfo *aitop, int reuse) +{ + evutil_socket_t fd; + + int on = 1, r; + int serrno; + + /* Create listen socket */ + fd = socket(aitop ? aitop->ai_family : AF_INET, SOCK_STREAM, 0); + if (fd == -1) { + event_sock_warn(-1, "socket"); + return (-1); + } + + if (evutil_make_socket_nonblocking(fd) < 0) + goto out; + if (evutil_make_socket_closeonexec(fd) < 0) + goto out; + + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on)); + if (reuse) + evutil_make_listen_socket_reuseable(fd); + + if (aitop != NULL) { + r = bind(fd, aitop->ai_addr, aitop->ai_addrlen); + if (r == -1) + goto out; + } + + return (fd); + + out: + serrno = EVUTIL_SOCKET_ERROR(); + evutil_closesocket(fd); + EVUTIL_SET_SOCKET_ERROR(serrno); + return (-1); +} + +static struct evutil_addrinfo * +make_addrinfo(const char *address, ev_uint16_t port) +{ + struct evutil_addrinfo *ai = NULL; + + struct evutil_addrinfo hints; + char strport[NI_MAXSERV]; + int ai_result; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + /* turn NULL hostname into INADDR_ANY, and skip looking up any address + * types we don't have an interface to connect to. */ + hints.ai_flags = EVUTIL_AI_PASSIVE|EVUTIL_AI_ADDRCONFIG; + evutil_snprintf(strport, sizeof(strport), "%d", port); + if ((ai_result = evutil_getaddrinfo(address, strport, &hints, &ai)) + != 0) { + if (ai_result == EVUTIL_EAI_SYSTEM) + event_warn("getaddrinfo"); + else + event_warnx("getaddrinfo: %s", + evutil_gai_strerror(ai_result)); + return (NULL); + } + + return (ai); +} + +static evutil_socket_t +bind_socket(const char *address, ev_uint16_t port, int reuse) +{ + evutil_socket_t fd; + struct evutil_addrinfo *aitop = NULL; + + /* just create an unbound socket */ + if (address == NULL && port == 0) + return bind_socket_ai(NULL, 0); + + aitop = make_addrinfo(address, port); + + if (aitop == NULL) + return (-1); + + fd = bind_socket_ai(aitop, reuse); + + evutil_freeaddrinfo(aitop); + + return (fd); +} + +static struct evhttp_bound_socket * +my_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t port) +{ + evutil_socket_t fd; + struct evhttp_bound_socket *bound; + + if ((fd = bind_socket(address, port, 1 /*reuse*/)) == -1) + return (NULL); + + if (listen(fd, 128) == -1) { + event_sock_warn(fd, "%s: listen", __func__); + evutil_closesocket(fd); + return (NULL); + } + + bound = evhttp_accept_socket_with_handle(http, fd); + + if (bound != NULL) { + event_debug(("Bound to port %d - Awaiting connections ... ", + port)); + return (bound); + } + + return (NULL); +} + /** Bind HTTP server to specified addresses */ static bool HTTPBindAddresses(struct evhttp* http) { @@ -387,7 +511,7 @@ static bool HTTPBindAddresses(struct evhttp* http) int num_fail = 0; for (std::vector >::iterator i = endpoints.begin(); i != endpoints.end(); ++i) { LogPrintf("Binding RPC on address %s port %i\n", i->first, i->second); - evhttp_bound_socket *bind_handle = evhttp_bind_socket_with_handle(http, i->first.empty() ? nullptr : i->first.c_str(), i->second); + evhttp_bound_socket *bind_handle = my_bind_socket_with_handle(http, i->first.empty() ? nullptr : i->first.c_str(), i->second); if (bind_handle) { const std::optional addr{LookupHost(i->first, false)}; if (i->first.empty() || (addr.has_value() && addr->IsBindAny())) { From 1a6dd1ff1b8aa7e1298c1055b75a18dce8864009 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 28 Dec 2018 05:29:34 +0000 Subject: [PATCH 02/11] httpserver: Preserve socket error from listen across closesocket cleanup --- src/httpserver.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 9dc274e98b..2f63f6558e 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -459,13 +459,16 @@ my_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t { evutil_socket_t fd; struct evhttp_bound_socket *bound; + int serrno; if ((fd = bind_socket(address, port, 1 /*reuse*/)) == -1) return (NULL); if (listen(fd, 128) == -1) { + serrno = EVUTIL_SOCKET_ERROR(); event_sock_warn(fd, "%s: listen", __func__); evutil_closesocket(fd); + EVUTIL_SET_SOCKET_ERROR(serrno); return (NULL); } From 0d1697421d39049e1fe6a08b8d1a55ab1951e7f7 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 28 Dec 2018 05:34:20 +0000 Subject: [PATCH 03/11] httpserver: Collapse bind_socket into my_bind_socket_with_handle --- src/httpserver.cpp | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 2f63f6558e..12ed7aeb6a 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -432,28 +432,6 @@ make_addrinfo(const char *address, ev_uint16_t port) return (ai); } -static evutil_socket_t -bind_socket(const char *address, ev_uint16_t port, int reuse) -{ - evutil_socket_t fd; - struct evutil_addrinfo *aitop = NULL; - - /* just create an unbound socket */ - if (address == NULL && port == 0) - return bind_socket_ai(NULL, 0); - - aitop = make_addrinfo(address, port); - - if (aitop == NULL) - return (-1); - - fd = bind_socket_ai(aitop, reuse); - - evutil_freeaddrinfo(aitop); - - return (fd); -} - static struct evhttp_bound_socket * my_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t port) { @@ -461,7 +439,23 @@ my_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t struct evhttp_bound_socket *bound; int serrno; - if ((fd = bind_socket(address, port, 1 /*reuse*/)) == -1) + struct evutil_addrinfo *aitop = NULL; + + /* just create an unbound socket */ + if (address == NULL && port == 0) { + fd = bind_socket_ai(NULL, 0); + } else { + aitop = make_addrinfo(address, port); + + if (aitop == NULL) { + return nullptr; + } + + fd = bind_socket_ai(aitop, 1 /*reuse*/); + + evutil_freeaddrinfo(aitop); + } + if (fd == -1) return (NULL); if (listen(fd, 128) == -1) { From c32389684364863c14f4270de791c1f861e708c6 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 28 Dec 2018 05:36:54 +0000 Subject: [PATCH 04/11] httpserver: Collapse make_addrinfo into my_bind_socket_with_handle --- src/httpserver.cpp | 49 ++++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 12ed7aeb6a..dd09631f4b 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -403,35 +403,6 @@ bind_socket_ai(struct evutil_addrinfo *aitop, int reuse) return (-1); } -static struct evutil_addrinfo * -make_addrinfo(const char *address, ev_uint16_t port) -{ - struct evutil_addrinfo *ai = NULL; - - struct evutil_addrinfo hints; - char strport[NI_MAXSERV]; - int ai_result; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - /* turn NULL hostname into INADDR_ANY, and skip looking up any address - * types we don't have an interface to connect to. */ - hints.ai_flags = EVUTIL_AI_PASSIVE|EVUTIL_AI_ADDRCONFIG; - evutil_snprintf(strport, sizeof(strport), "%d", port); - if ((ai_result = evutil_getaddrinfo(address, strport, &hints, &ai)) - != 0) { - if (ai_result == EVUTIL_EAI_SYSTEM) - event_warn("getaddrinfo"); - else - event_warnx("getaddrinfo: %s", - evutil_gai_strerror(ai_result)); - return (NULL); - } - - return (ai); -} - static struct evhttp_bound_socket * my_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t port) { @@ -445,7 +416,25 @@ my_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t if (address == NULL && port == 0) { fd = bind_socket_ai(NULL, 0); } else { - aitop = make_addrinfo(address, port); + struct evutil_addrinfo hints; + char strport[NI_MAXSERV]; + int ai_result; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + /* turn NULL hostname into INADDR_ANY, and skip looking up any address + * types we don't have an interface to connect to. */ + hints.ai_flags = EVUTIL_AI_PASSIVE|EVUTIL_AI_ADDRCONFIG; + evutil_snprintf(strport, sizeof(strport), "%d", port); + if ((ai_result = evutil_getaddrinfo(address, strport, &hints, &aitop)) != 0) { + if (ai_result == EVUTIL_EAI_SYSTEM) { + event_warn("getaddrinfo"); + } else { + event_warnx("getaddrinfo: %s", evutil_gai_strerror(ai_result)); + } + aitop = nullptr; + } if (aitop == NULL) { return nullptr; From bb77b84f0bfc93f173afdcb159b24d148a4c0d1f Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 28 Dec 2018 05:39:20 +0000 Subject: [PATCH 05/11] httpserver: Only make a single bind_socket_ai call from my_bind_socket_with_handle --- src/httpserver.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/httpserver.cpp b/src/httpserver.cpp index dd09631f4b..c8cac2f0af 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -412,9 +412,9 @@ my_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t struct evutil_addrinfo *aitop = NULL; - /* just create an unbound socket */ + int reuse; if (address == NULL && port == 0) { - fd = bind_socket_ai(NULL, 0); + reuse = 0; } else { struct evutil_addrinfo hints; char strport[NI_MAXSERV]; @@ -440,10 +440,10 @@ my_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t return nullptr; } - fd = bind_socket_ai(aitop, 1 /*reuse*/); - - evutil_freeaddrinfo(aitop); + reuse = 1; } + fd = bind_socket_ai(aitop, reuse); + if (aitop) evutil_freeaddrinfo(aitop); if (fd == -1) return (NULL); From ba4f928ff7b710927144bd9ed83644f3fe5454cf Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 28 Dec 2018 05:49:33 +0000 Subject: [PATCH 06/11] httpserver: Collapse bind_socket_ai into my_bind_socket_with_handle --- src/httpserver.cpp | 70 +++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 42 deletions(-) diff --git a/src/httpserver.cpp b/src/httpserver.cpp index c8cac2f0af..57c083b2e0 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -362,47 +362,6 @@ static void ThreadHTTP(struct event_base* base) #define event_warnx LogPrintf #define event_sock_warn(sock, ...) LogPrintf(__VA_ARGS__) -/* Create a non-blocking socket and bind it */ -/* todo: rename this function */ -static evutil_socket_t -bind_socket_ai(struct evutil_addrinfo *aitop, int reuse) -{ - evutil_socket_t fd; - - int on = 1, r; - int serrno; - - /* Create listen socket */ - fd = socket(aitop ? aitop->ai_family : AF_INET, SOCK_STREAM, 0); - if (fd == -1) { - event_sock_warn(-1, "socket"); - return (-1); - } - - if (evutil_make_socket_nonblocking(fd) < 0) - goto out; - if (evutil_make_socket_closeonexec(fd) < 0) - goto out; - - setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on)); - if (reuse) - evutil_make_listen_socket_reuseable(fd); - - if (aitop != NULL) { - r = bind(fd, aitop->ai_addr, aitop->ai_addrlen); - if (r == -1) - goto out; - } - - return (fd); - - out: - serrno = EVUTIL_SOCKET_ERROR(); - evutil_closesocket(fd); - EVUTIL_SET_SOCKET_ERROR(serrno); - return (-1); -} - static struct evhttp_bound_socket * my_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t port) { @@ -442,7 +401,34 @@ my_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t reuse = 1; } - fd = bind_socket_ai(aitop, reuse); + + int on = 1, r; + + /* Create listen socket */ + fd = socket(aitop ? aitop->ai_family : AF_INET, SOCK_STREAM, 0); + if (fd == -1) { + event_sock_warn(-1, "socket"); + } else if (evutil_make_socket_nonblocking(fd) < 0) { +out: + serrno = EVUTIL_SOCKET_ERROR(); + evutil_closesocket(fd); + EVUTIL_SET_SOCKET_ERROR(serrno); + fd = -1; + } else { + if (evutil_make_socket_closeonexec(fd) < 0) + goto out; + + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on)); + if (reuse) + evutil_make_listen_socket_reuseable(fd); + + if (aitop != NULL) { + r = bind(fd, aitop->ai_addr, aitop->ai_addrlen); + if (r == -1) + goto out; + } + } + if (aitop) evutil_freeaddrinfo(aitop); if (fd == -1) return (NULL); From eb2c571571012a47804f8555009ffb37ca999911 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 28 Dec 2018 06:00:22 +0000 Subject: [PATCH 07/11] httpserver: Adapt my_bind_socket_with_handle to local code style --- src/httpserver.cpp | 76 +++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 45 deletions(-) diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 57c083b2e0..c477eca6a3 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -356,27 +356,20 @@ static void ThreadHTTP(struct event_base* base) LogPrint(BCLog::HTTP, "Exited http event loop\n"); } -// Temporary hacks to avoid modifying libevent code yet -#define event_debug(x) LogPrintf x -#define event_warn LogPrintf -#define event_warnx LogPrintf -#define event_sock_warn(sock, ...) LogPrintf(__VA_ARGS__) - static struct evhttp_bound_socket * my_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t port) { - evutil_socket_t fd; - struct evhttp_bound_socket *bound; - int serrno; + evutil_socket_t fd; + struct evhttp_bound_socket *bound; + int serrno; - struct evutil_addrinfo *aitop = NULL; + struct evutil_addrinfo *aitop = nullptr; int reuse; - if (address == NULL && port == 0) { + if (address == nullptr && port == 0) { reuse = 0; } else { struct evutil_addrinfo hints; - char strport[NI_MAXSERV]; int ai_result; memset(&hints, 0, sizeof(hints)); @@ -385,29 +378,27 @@ my_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t /* turn NULL hostname into INADDR_ANY, and skip looking up any address * types we don't have an interface to connect to. */ hints.ai_flags = EVUTIL_AI_PASSIVE|EVUTIL_AI_ADDRCONFIG; - evutil_snprintf(strport, sizeof(strport), "%d", port); - if ((ai_result = evutil_getaddrinfo(address, strport, &hints, &aitop)) != 0) { + const std::string strport = strprintf("%d", port); + if ((ai_result = evutil_getaddrinfo(address, strport.c_str(), &hints, &aitop)) != 0) { if (ai_result == EVUTIL_EAI_SYSTEM) { - event_warn("getaddrinfo"); + LogPrintf("libevent: getaddrinfo\n"); } else { - event_warnx("getaddrinfo: %s", evutil_gai_strerror(ai_result)); + LogPrintf("libevent: getaddrinfo: %s\n", strerror(errno)); } aitop = nullptr; } - if (aitop == NULL) { + if (aitop == nullptr) { return nullptr; } reuse = 1; } - int on = 1, r; - /* Create listen socket */ fd = socket(aitop ? aitop->ai_family : AF_INET, SOCK_STREAM, 0); if (fd == -1) { - event_sock_warn(-1, "socket"); + LogPrintf("libevent: socket: %s\n", evutil_socket_error_to_string(evutil_socket_geterror(-1))); } else if (evutil_make_socket_nonblocking(fd) < 0) { out: serrno = EVUTIL_SOCKET_ERROR(); @@ -415,41 +406,36 @@ out: EVUTIL_SET_SOCKET_ERROR(serrno); fd = -1; } else { - if (evutil_make_socket_closeonexec(fd) < 0) - goto out; + if (evutil_make_socket_closeonexec(fd) < 0) goto out; - setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on)); - if (reuse) - evutil_make_listen_socket_reuseable(fd); + const int on = 1; + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (sockopt_arg_type)&on, sizeof(on)); + if (reuse) evutil_make_listen_socket_reuseable(fd); - if (aitop != NULL) { - r = bind(fd, aitop->ai_addr, aitop->ai_addrlen); - if (r == -1) - goto out; + if (aitop != nullptr) { + if (bind(fd, aitop->ai_addr, aitop->ai_addrlen) == -1) goto out; } } if (aitop) evutil_freeaddrinfo(aitop); - if (fd == -1) - return (NULL); + if (fd == -1) return nullptr; - if (listen(fd, 128) == -1) { - serrno = EVUTIL_SOCKET_ERROR(); - event_sock_warn(fd, "%s: listen", __func__); - evutil_closesocket(fd); - EVUTIL_SET_SOCKET_ERROR(serrno); - return (NULL); - } + if (listen(fd, 128) == -1) { + serrno = EVUTIL_SOCKET_ERROR(); + LogPrintf("libevent: %s: listen\n", __func__); + evutil_closesocket(fd); + EVUTIL_SET_SOCKET_ERROR(serrno); + return nullptr; + } - bound = evhttp_accept_socket_with_handle(http, fd); + bound = evhttp_accept_socket_with_handle(http, fd); - if (bound != NULL) { - event_debug(("Bound to port %d - Awaiting connections ... ", - port)); - return (bound); - } + if (bound != nullptr) { + LogPrint(BCLog::LIBEVENT, "libevent: Bound to port %d - Awaiting connections ... \n", port); + return bound; + } - return (NULL); + return nullptr; } /** Bind HTTP server to specified addresses */ From de303675e4c1546455fb85fcb3be73b4c5cecb8b Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 28 Dec 2018 06:18:43 +0000 Subject: [PATCH 08/11] httpserver: Simplify my_bind_socket_with_handle --- src/httpserver.cpp | 71 ++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 43 deletions(-) diff --git a/src/httpserver.cpp b/src/httpserver.cpp index c477eca6a3..fcbdbcf4a0 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -365,77 +365,62 @@ my_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t struct evutil_addrinfo *aitop = nullptr; - int reuse; if (address == nullptr && port == 0) { - reuse = 0; + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd == -1) { + LogPrintf("libevent: socket: %s\n", evutil_socket_error_to_string(evutil_socket_geterror(-1))); + return nullptr; + } } else { - struct evutil_addrinfo hints; + struct evutil_addrinfo hints = {}; int ai_result; - memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; - /* turn NULL hostname into INADDR_ANY, and skip looking up any address - * types we don't have an interface to connect to. */ hints.ai_flags = EVUTIL_AI_PASSIVE|EVUTIL_AI_ADDRCONFIG; const std::string strport = strprintf("%d", port); - if ((ai_result = evutil_getaddrinfo(address, strport.c_str(), &hints, &aitop)) != 0) { + ai_result = evutil_getaddrinfo(address, strport.c_str(), &hints, &aitop); + if (ai_result || !aitop) { if (ai_result == EVUTIL_EAI_SYSTEM) { LogPrintf("libevent: getaddrinfo\n"); - } else { - LogPrintf("libevent: getaddrinfo: %s\n", strerror(errno)); + } else if (ai_result) { + LogPrintf("libevent: getaddrinfo: %s\n", evutil_gai_strerror(ai_result)); } - aitop = nullptr; - } - - if (aitop == nullptr) { return nullptr; } - reuse = 1; - } - - /* Create listen socket */ - fd = socket(aitop ? aitop->ai_family : AF_INET, SOCK_STREAM, 0); - if (fd == -1) { - LogPrintf("libevent: socket: %s\n", evutil_socket_error_to_string(evutil_socket_geterror(-1))); - } else if (evutil_make_socket_nonblocking(fd) < 0) { -out: - serrno = EVUTIL_SOCKET_ERROR(); - evutil_closesocket(fd); - EVUTIL_SET_SOCKET_ERROR(serrno); - fd = -1; - } else { - if (evutil_make_socket_closeonexec(fd) < 0) goto out; - - const int on = 1; - setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (sockopt_arg_type)&on, sizeof(on)); - if (reuse) evutil_make_listen_socket_reuseable(fd); - - if (aitop != nullptr) { - if (bind(fd, aitop->ai_addr, aitop->ai_addrlen) == -1) goto out; + fd = socket(aitop->ai_family, SOCK_STREAM, 0); + if (fd == -1) { + evutil_freeaddrinfo(aitop); + return nullptr; } + evutil_make_listen_socket_reuseable(fd); } - if (aitop) evutil_freeaddrinfo(aitop); - if (fd == -1) return nullptr; + const int on = 1; + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (sockopt_arg_type)&on, sizeof(on)); - if (listen(fd, 128) == -1) { + bool listen_failed = false; + if (evutil_make_socket_nonblocking(fd) < 0 || + evutil_make_socket_closeonexec(fd) < 0 || + (aitop && bind(fd, aitop->ai_addr, aitop->ai_addrlen) == -1) || + (listen_failed = (listen(fd, 128) == -1)) + ) { serrno = EVUTIL_SOCKET_ERROR(); - LogPrintf("libevent: %s: listen\n", __func__); + if (listen_failed) LogPrintf("libevent: %s: listen\n", __func__); evutil_closesocket(fd); + if (aitop) evutil_freeaddrinfo(aitop); EVUTIL_SET_SOCKET_ERROR(serrno); return nullptr; } - bound = evhttp_accept_socket_with_handle(http, fd); + if (aitop) evutil_freeaddrinfo(aitop); + bound = evhttp_accept_socket_with_handle(http, fd); if (bound != nullptr) { LogPrint(BCLog::LIBEVENT, "libevent: Bound to port %d - Awaiting connections ... \n", port); - return bound; } - - return nullptr; + return bound; } /** Bind HTTP server to specified addresses */ From e8461128b8d63cc3ff1364d860b45de7aa79e02a Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 28 Dec 2018 07:28:17 +0000 Subject: [PATCH 09/11] httpserver: Check for and tolerate more possible system limitations --- src/httpserver.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/httpserver.cpp b/src/httpserver.cpp index fcbdbcf4a0..011aa148ba 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -357,7 +357,7 @@ static void ThreadHTTP(struct event_base* base) } static struct evhttp_bound_socket * -my_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t port) +my_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t port, bool& ignorable_error) { evutil_socket_t fd; struct evhttp_bound_socket *bound; @@ -381,9 +381,18 @@ my_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t const std::string strport = strprintf("%d", port); ai_result = evutil_getaddrinfo(address, strport.c_str(), &hints, &aitop); if (ai_result || !aitop) { - if (ai_result == EVUTIL_EAI_SYSTEM) { + switch (ai_result) { + case 0: + break; + case EVUTIL_EAI_SYSTEM: LogPrintf("libevent: getaddrinfo\n"); - } else if (ai_result) { + break; + case EVUTIL_EAI_ADDRFAMILY: + case EVUTIL_EAI_FAMILY: + case EVUTIL_EAI_SOCKTYPE: + ignorable_error = true; + break; + default: LogPrintf("libevent: getaddrinfo: %s\n", evutil_gai_strerror(ai_result)); } return nullptr; @@ -454,7 +463,8 @@ static bool HTTPBindAddresses(struct evhttp* http) int num_fail = 0; for (std::vector >::iterator i = endpoints.begin(); i != endpoints.end(); ++i) { LogPrintf("Binding RPC on address %s port %i\n", i->first, i->second); - evhttp_bound_socket *bind_handle = my_bind_socket_with_handle(http, i->first.empty() ? nullptr : i->first.c_str(), i->second); + bool ignorable_error = false; + evhttp_bound_socket *bind_handle = my_bind_socket_with_handle(http, i->first.empty() ? nullptr : i->first.c_str(), i->second, ignorable_error); if (bind_handle) { const std::optional addr{LookupHost(i->first, false)}; if (i->first.empty() || (addr.has_value() && addr->IsBindAny())) { @@ -463,7 +473,7 @@ static bool HTTPBindAddresses(struct evhttp* http) boundSockets.push_back(bind_handle); } else { int err = EVUTIL_SOCKET_ERROR(); - if (!is_default || (err != EADDRNOTAVAIL && err != ENOENT)) { + if (!is_default || (err != EADDRNOTAVAIL && err != ENOENT && err != EOPNOTSUPP && !ignorable_error)) { LogPrintf("Binding RPC on address %s port %i failed (Error: %s).\n", i->first, i->second, NetworkErrorString(err)); num_fail += 1; } else { From 5a067073d7765f689da4df7a208cd753f635dd60 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sun, 28 Apr 2019 22:36:38 +0000 Subject: [PATCH 10/11] Bugfix: httpserver: Close listen socket if we fail to make an evhttp handle --- src/httpserver.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 011aa148ba..0e094e9a37 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -426,9 +426,12 @@ my_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t if (aitop) evutil_freeaddrinfo(aitop); bound = evhttp_accept_socket_with_handle(http, fd); - if (bound != nullptr) { - LogPrint(BCLog::LIBEVENT, "libevent: Bound to port %d - Awaiting connections ... \n", port); + if (bound == nullptr) { + evutil_closesocket(fd); + return nullptr; } + + LogPrint(BCLog::LIBEVENT, "libevent: Bound to port %d - Awaiting connections ... \n", port); return bound; } From 45dd91f71f4205022e1cd80d1c410a5dbd89cc95 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sat, 7 Mar 2020 02:47:39 +0000 Subject: [PATCH 11/11] httpserver: Workaround libevent bug by tolerating specific failure to support IPv6 binds (for default bind only) --- src/httpserver.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 0e094e9a37..6975fd061f 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -387,6 +387,9 @@ my_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t case EVUTIL_EAI_SYSTEM: LogPrintf("libevent: getaddrinfo\n"); break; + case EVUTIL_EAI_NODATA: + LogPrintf("evutil_getaddrinfo doesn't support IPv6; cannot bind %s:%d\n", address, port); + [[fallthrough]]; case EVUTIL_EAI_ADDRFAMILY: case EVUTIL_EAI_FAMILY: case EVUTIL_EAI_SOCKTYPE: