summaryrefslogtreecommitdiff
path: root/methods
diff options
context:
space:
mode:
authorJulian Andres Klode <jak@debian.org>2018-01-02 21:43:52 +0100
committerJulian Andres Klode <jak@debian.org>2018-01-03 15:23:55 +0100
commit23ddea7485ea5d28b8ad1a0d35e0d8c4de7ad54b (patch)
treecdcf62c7848fee369d0f493ec5378b8f47af419b /methods
parent2bcc73f4392136a702f5e3dd9234addefd8a7f9c (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
Diffstat (limited to 'methods')
-rw-r--r--methods/connect.cc72
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)