diff --git a/CMakeLists.txt b/CMakeLists.txt index 6000d455..56b13ab7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -704,4 +704,6 @@ add_test(DownloaderTest COMMAND downloadertest) add_executable(jsontest test/json.cpp ${BINARY_DIR}/mist/.headers) target_link_libraries(jsontest mist) add_test(JSONTest COMMAND jsontest) +add_executable(resolvetest test/resolve.cpp ${BINARY_DIR}/mist/.headers) +target_link_libraries(resolvetest mist) 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); diff --git a/test/resolve.cpp b/test/resolve.cpp new file mode 100644 index 00000000..de75a180 --- /dev/null +++ b/test/resolve.cpp @@ -0,0 +1,20 @@ +#include "../lib/socket.cpp" +#include +#include +#include + +int main(int argc, char **argv){ + if (argc < 2){return 1;} + if (argc > 2){ + std::cout << "Best IPv4 guess:" + << Socket::resolveHostToBestExternalAddrGuess(argv[1], AF_INET, argv[2]) << std::endl; + std::cout << "Best IPv6 guess:" + << Socket::resolveHostToBestExternalAddrGuess(argv[1], AF_INET6, argv[2]) << std::endl; + }else{ + std::cout << "Best IPv4 guess:" << Socket::resolveHostToBestExternalAddrGuess(argv[1], AF_INET) + << std::endl; + std::cout << "Best IPv6 guess:" << Socket::resolveHostToBestExternalAddrGuess(argv[1], AF_INET6) + << std::endl; + } + return 0; +}