From 5b95de663e6cee3b84c844790e96348017970bb5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 5 May 2018 22:52:57 -0700 Subject: [PATCH] net: Listen on ipv6 and ipv4. Hurray, no longer "part of the problem" for ipv4. --- UI/RemoteISOScreen.cpp | 27 +++++++++---- ext/native/net/http_server.cpp | 74 +++++++++++++++++++++++++++++++--- ext/native/net/http_server.h | 6 ++- 3 files changed, 93 insertions(+), 14 deletions(-) diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index b5ad54498cd6..774946905a75 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -69,25 +69,36 @@ static ServerStatus RetrieveStatus() { // relay that address to a mobile device searching for the server. static void RegisterServer(int port) { http::Client http; + Buffer theVoid; - char resource[1024] = {}; - // We register both IPv4 and IPv6 in case the other client is using a different one. - // Our server is (currently?) ipv4 only, so we always register the ipv4 local for now. + char resource4[1024] = {}; if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT, net::DNSType::IPV4)) { - Buffer theVoid; if (http.Connect(2, 20.0, &scanCancelled)) { std::string ip = fd_util::GetLocalIP(http.sock()); - snprintf(resource, sizeof(resource) - 1, "/match/update?local=%s&port=%d", ip.c_str(), port); + snprintf(resource4, sizeof(resource4) - 1, "/match/update?local=%s&port=%d", ip.c_str(), port); - http.GET(resource, &theVoid); + http.GET(resource4, &theVoid); + theVoid.Skip(theVoid.size()); http.Disconnect(); } } if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT, net::DNSType::IPV6)) { - Buffer theVoid; + // We register both IPv4 and IPv6 in case the other client is using a different one. + if (resource4[0] != 0 && http.Connect()) { + http.GET(resource4, &theVoid); + theVoid.Skip(theVoid.size()); + http.Disconnect(); + } + + // Currently, we're not using keepalive, so gotta reconnect... if (http.Connect()) { - http.GET(resource, &theVoid); + char resource6[1024] = {}; + std::string ip = fd_util::GetLocalIP(http.sock()); + snprintf(resource6, sizeof(resource6) - 1, "/match/update?local=%s&port=%d", ip.c_str(), port); + + http.GET(resource6, &theVoid); + theVoid.Skip(theVoid.size()); http.Disconnect(); } } diff --git a/ext/native/net/http_server.cpp b/ext/native/net/http_server.cpp index 9357d2fe6967..ddd893e5ac3a 100644 --- a/ext/native/net/http_server.cpp +++ b/ext/native/net/http_server.cpp @@ -130,9 +130,21 @@ void Server::SetFallbackHandler(UrlHandlerFunc handler) { fallback_ = handler; } -bool Server::Listen(int port) { +bool Server::Listen(int port, net::DNSType type) { + bool success = false; + if (type == net::DNSType::ANY || type == net::DNSType::IPV6) { + success = Listen6(port, type == net::DNSType::IPV6); + } + if (!success && (type == net::DNSType::ANY || type == net::DNSType::IPV4)) { + success = Listen4(port); + } + return success; +} + +bool Server::Listen4(int port) { listener_ = socket(AF_INET, SOCK_STREAM, 0); - CHECK_GE(listener_, 0); + if (listener_ < 0) + return false; struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); @@ -145,6 +157,7 @@ bool Server::Listen(int port) { setsockopt(listener_, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt)); if (bind(listener_, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { + closesocket(listener_); ELOG("Failed to bind to port %i. Bailing.", port); return false; } @@ -152,7 +165,10 @@ bool Server::Listen(int port) { fd_util::SetNonBlocking(listener_, true); // 1024 is the max number of queued requests. - CHECK_GE(listen(listener_, 1024), 0); + if (listen(listener_, 1024) < 0) { + closesocket(listener_); + return false; + } socklen_t len = sizeof(server_addr); if (getsockname(listener_, (struct sockaddr *)&server_addr, &len) == 0) { @@ -165,6 +181,50 @@ bool Server::Listen(int port) { return true; } +bool Server::Listen6(int port, bool ipv6_only) { + listener_ = socket(AF_INET6, SOCK_STREAM, 0); + if (listener_ < 0) + return false; + + struct sockaddr_in6 server_addr; + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin6_family = AF_INET6; + server_addr.sin6_addr = in6addr_any; + server_addr.sin6_port = htons(port); + + int opt = 1; + // Enable re-binding to avoid the pain when restarting the server quickly. + setsockopt(listener_, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt)); + + // Enable listening on IPv6 and IPv4? + opt = ipv6_only ? 1 : 0; + setsockopt(listener_, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&opt, sizeof(opt)); + + if (bind(listener_, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { + closesocket(listener_); + ELOG("Failed to bind to port %i. Bailing.", port); + return false; + } + + fd_util::SetNonBlocking(listener_, true); + + // 1024 is the max number of queued requests. + if (listen(listener_, 1024) < 0) { + closesocket(listener_); + return false; + } + + socklen_t len = sizeof(server_addr); + if (getsockname(listener_, (struct sockaddr *)&server_addr, &len) == 0) { + port = ntohs(server_addr.sin6_port); + } + + ILOG("HTTP server started on port %i", port); + port_ = port; + + return true; +} + bool Server::RunSlice(double timeout) { if (listener_ < 0 || port_ == 0) { return false; @@ -177,9 +237,13 @@ bool Server::RunSlice(double timeout) { return false; } - sockaddr client_addr; + union { + struct sockaddr sa; + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + } client_addr; socklen_t client_addr_size = sizeof(client_addr); - int conn_fd = accept(listener_, &client_addr, &client_addr_size); + int conn_fd = accept(listener_, &client_addr.sa, &client_addr_size); if (conn_fd >= 0) { executor_->Run(std::bind(&Server::HandleConnection, this, conn_fd)); return true; diff --git a/ext/native/net/http_server.h b/ext/native/net/http_server.h index 29ba802618eb..5220bda7622a 100644 --- a/ext/native/net/http_server.h +++ b/ext/native/net/http_server.h @@ -6,6 +6,7 @@ #include "base/buffer.h" #include "net/http_headers.h" +#include "net/resolve.h" #include "thread/executor.h" namespace net { @@ -73,7 +74,7 @@ class Server { // May run for (significantly) longer than timeout, but won't wait longer than that // for a new connection to handle. bool RunSlice(double timeout); - bool Listen(int port); + bool Listen(int port, net::DNSType type = net::DNSType::ANY); void Stop(); void RegisterHandler(const char *url_path, UrlHandlerFunc handler); @@ -89,6 +90,9 @@ class Server { } private: + bool Listen6(int port, bool ipv6_only); + bool Listen4(int port); + void HandleConnection(int conn_fd); // Things like default 404, etc.