diff options
author | Julian Andres Klode <jak@debian.org> | 2018-01-02 21:43:52 +0100 |
---|---|---|
committer | Julian Andres Klode <jak@debian.org> | 2018-01-03 15:23:55 +0100 |
commit | 23ddea7485ea5d28b8ad1a0d35e0d8c4de7ad54b (patch) | |
tree | cdcf62c7848fee369d0f493ec5378b8f47af419b | |
parent | 2bcc73f4392136a702f5e3dd9234addefd8a7f9c (diff) |
connect: Alternate address families for addresses
As a first step to implementing Happy Eyeballs version 2, we
need to order the list of hosts getaddrinfo() gave us so it
alternates between preferred and other address families.
RFC: https://tools.ietf.org/html/rfc8305
Gbp-Dch: ignore
-rw-r--r-- | methods/connect.cc | 72 |
1 files changed, 48 insertions, 24 deletions
diff --git a/methods/connect.cc b/methods/connect.cc index 1354fe97b..22dd41bee 100644 --- a/methods/connect.cc +++ b/methods/connect.cc @@ -198,6 +198,50 @@ static ResultState DoConnect(struct addrinfo *Addr, std::string const &Host, return ResultState::SUCCESSFUL; } /*}}}*/ +// Order the given host names returned by getaddrinfo() /*{{{*/ +static std::vector<struct addrinfo *> OrderAddresses(struct addrinfo *CurHost) +{ + std::vector<struct addrinfo *> preferredAddrs; + std::vector<struct addrinfo *> otherAddrs; + std::vector<struct addrinfo *> allAddrs; + + // Partition addresses into preferred and other address families + while (CurHost != 0) + { + if (preferredAddrs.empty() || CurHost->ai_family == preferredAddrs[0]->ai_family) + preferredAddrs.push_back(CurHost); + else + otherAddrs.push_back(CurHost); + + // Ignore UNIX domain sockets + do + { + CurHost = CurHost->ai_next; + } while (CurHost != 0 && CurHost->ai_family == AF_UNIX); + + /* If we reached the end of the search list then wrap around to the + start */ + if (CurHost == 0 && LastUsed != 0) + CurHost = LastHostAddr; + + // Reached the end of the search cycle + if (CurHost == LastUsed) + break; + } + + // Build a new address vector alternating between preferred and other + for (auto prefIter = preferredAddrs.cbegin(), otherIter = otherAddrs.cbegin(); + prefIter != preferredAddrs.end() || otherIter != otherAddrs.end();) + { + if (prefIter != preferredAddrs.end()) + allAddrs.push_back(*prefIter++); + if (otherIter != otherAddrs.end()) + allAddrs.push_back(*otherIter++); + } + + return std::move(allAddrs); +} + /*}}}*/ // Connect to a given Hostname /*{{{*/ static ResultState ConnectToHostname(std::string const &Host, int const Port, const char *const Service, int DefPort, std::unique_ptr<MethodFd> &Fd, @@ -301,12 +345,11 @@ static ResultState ConnectToHostname(std::string const &Host, int const Port, } // When we have an IP rotation stay with the last IP. - struct addrinfo *CurHost = LastHostAddr; - if (LastUsed != 0) - CurHost = LastUsed; - - while (CurHost != 0) + auto Addresses = OrderAddresses(LastUsed != nullptr ? LastUsed : LastHostAddr); + + for (auto CurHost : Addresses) { + _error->Discard(); auto const result = DoConnect(CurHost, Host, TimeOut, Fd, Owner); if (result == ResultState::SUCCESSFUL) { @@ -314,25 +357,6 @@ static ResultState ConnectToHostname(std::string const &Host, int const Port, return result; } Fd->Close(); - - // Ignore UNIX domain sockets - do - { - CurHost = CurHost->ai_next; - } - while (CurHost != 0 && CurHost->ai_family == AF_UNIX); - - /* If we reached the end of the search list then wrap around to the - start */ - if (CurHost == 0 && LastUsed != 0) - CurHost = LastHostAddr; - - // Reached the end of the search cycle - if (CurHost == LastUsed) - break; - - if (CurHost != 0) - _error->Discard(); } if (_error->PendingError() == true) |