diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 687a5550ea..6975fd061f 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -356,6 +356,88 @@ static void ThreadHTTP(struct event_base* base) LogPrint(BCLog::HTTP, "Exited http event loop\n"); } +static struct evhttp_bound_socket * +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; + int serrno; + + struct evutil_addrinfo *aitop = nullptr; + + if (address == nullptr && port == 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 = {}; + int ai_result; + + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = EVUTIL_AI_PASSIVE|EVUTIL_AI_ADDRCONFIG; + const std::string strport = strprintf("%d", port); + ai_result = evutil_getaddrinfo(address, strport.c_str(), &hints, &aitop); + if (ai_result || !aitop) { + switch (ai_result) { + case 0: + break; + 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: + ignorable_error = true; + break; + default: + LogPrintf("libevent: getaddrinfo: %s\n", evutil_gai_strerror(ai_result)); + } + return nullptr; + } + + fd = socket(aitop->ai_family, SOCK_STREAM, 0); + if (fd == -1) { + evutil_freeaddrinfo(aitop); + return nullptr; + } + evutil_make_listen_socket_reuseable(fd); + } + + const int on = 1; + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (sockopt_arg_type)&on, sizeof(on)); + + 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(); + if (listen_failed) LogPrintf("libevent: %s: listen\n", __func__); + evutil_closesocket(fd); + if (aitop) evutil_freeaddrinfo(aitop); + EVUTIL_SET_SOCKET_ERROR(serrno); + return nullptr; + } + + if (aitop) evutil_freeaddrinfo(aitop); + + bound = evhttp_accept_socket_with_handle(http, fd); + if (bound == nullptr) { + evutil_closesocket(fd); + return nullptr; + } + + LogPrint(BCLog::LIBEVENT, "libevent: Bound to port %d - Awaiting connections ... \n", port); + return bound; +} + /** Bind HTTP server to specified addresses */ static bool HTTPBindAddresses(struct evhttp* http) { @@ -387,7 +469,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 = evhttp_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())) { @@ -396,7 +479,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 {