From 0e7c7ce3dd6f5f89b0ce708d91d0ca19a25f2dae Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 2 Oct 2019 14:09:49 +0200 Subject: [PATCH] Backported socket library edits from LL branch --- lib/socket.cpp | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/socket.h | 3 +++ 2 files changed, 60 insertions(+) diff --git a/lib/socket.cpp b/lib/socket.cpp index 72d8245d..1b1c6ace 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -213,6 +213,44 @@ void Socket::hostBytesToStr(const char *bytes, size_t len, std::string &target){ } } +/// Resolves a hostname into a human-readable address that is the best guess for external address matching this host. +/// The optional family can force IPv4/IPv6 resolving, while the optional hint will allow forcing resolving to a +/// specific address if it is a match for this host. +/// Returns empty string if no reasonable match could be made. +std::string Socket::resolveHostToBestExternalAddrGuess(const std::string &host, int family, + const std::string &hint){ + struct addrinfo *result, *rp, hints; + std::string newaddr; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = family; + hints.ai_socktype = 0; + hints.ai_flags = AI_ADDRCONFIG; + int s = getaddrinfo(host.c_str(), 0, &hints, &result); + if (s != 0){ + FAIL_MSG("Could not resolve %s! Error: %s", host.c_str(), gai_strerror(s)); + return ""; + } + + for (rp = result; rp != NULL; rp = rp->ai_next){ + static char addrconv[INET6_ADDRSTRLEN]; + if (rp->ai_family == AF_INET6){ + newaddr = inet_ntop(rp->ai_family, &((const sockaddr_in6 *)rp->ai_addr)->sin6_addr, addrconv, INET6_ADDRSTRLEN); + } + if (rp->ai_family == AF_INET){ + newaddr = inet_ntop(rp->ai_family, &((const sockaddr_in *)rp->ai_addr)->sin_addr, addrconv, INET6_ADDRSTRLEN); + } + if (newaddr.substr(0, 7) == "::ffff:"){newaddr = newaddr.substr(7);} + HIGH_MSG("Resolved to %s addr [%s]", addrFam(rp->ai_family), newaddr.c_str()); + // if not a local address, we can't bind, so don't bother trying it + if (!isLocal(newaddr)){continue;} + // we match the hint, done! + if (newaddr == hint){break;} + } + freeaddrinfo(result); + return newaddr; +} + std::string uint2string(unsigned int i){ std::stringstream st; st << i; @@ -1646,6 +1684,25 @@ void Socket::UDPConnection::SendNow(const char *sdata, size_t len){ } } +std::string Socket::UDPConnection::getBoundAddress(){ + struct sockaddr_in6 tmpaddr; + socklen_t len = sizeof(tmpaddr); + std::string boundaddr; + if (!getsockname(sock, (sockaddr *)&tmpaddr, &len)){ + static char addrconv[INET6_ADDRSTRLEN]; + if (tmpaddr.sin6_family == AF_INET6){ + boundaddr = inet_ntop(AF_INET6, &(tmpaddr.sin6_addr), addrconv, INET6_ADDRSTRLEN); + if (boundaddr.substr(0, 7) == "::ffff:"){boundaddr = boundaddr.substr(7);} + HIGH_MSG("Local IPv6 addr [%s]", boundaddr.c_str()); + } + if (tmpaddr.sin6_family == AF_INET){ + boundaddr = inet_ntop(AF_INET, &(((sockaddr_in *)&tmpaddr)->sin_addr), addrconv, INET6_ADDRSTRLEN); + HIGH_MSG("Local IPv4 addr [%s]", boundaddr.c_str()); + } + } + return boundaddr; +} + /// Bind to a port number, returning the bound port. /// If that fails, returns zero. /// \arg port Port to bind to, required. diff --git a/lib/socket.h b/lib/socket.h index 880f6113..34f4bc4d 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -42,6 +42,8 @@ namespace Socket{ /// Returns true if given human-readable hostname is a local address. bool isLocalhost(const std::string &host); bool checkTrueSocket(int sock); + std::string resolveHostToBestExternalAddrGuess(const std::string &host, int family = AF_UNSPEC, + const std::string &hint = ""); /// A buffer made out of std::string objects that can be efficiently read from and written to. class Buffer{ @@ -205,6 +207,7 @@ namespace Socket{ void setBlocking(bool blocking); void SetDestination(std::string hostname, uint32_t port); void GetDestination(std::string &hostname, uint32_t &port); + std::string getBoundAddress(); uint32_t getDestPort() const; bool Receive(); void SendNow(const std::string &data);