summaryrefslogtreecommitdiff
path: root/methods
diff options
context:
space:
mode:
Diffstat (limited to 'methods')
-rw-r--r--methods/connect.cc126
1 files changed, 100 insertions, 26 deletions
diff --git a/methods/connect.cc b/methods/connect.cc
index 160491d96..334a1d3f3 100644
--- a/methods/connect.cc
+++ b/methods/connect.cc
@@ -23,6 +23,7 @@
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
+#include <list>
#include <set>
#include <sstream>
#include <string>
@@ -35,6 +36,7 @@
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
+#include <sys/select.h>
#include <sys/socket.h>
#include "aptmethod.h"
@@ -146,14 +148,19 @@ struct Connection
std::stringstream ss;
ioprintf(ss, _("[IP: %s %s]"), Name, Service);
Owner->SetIP(ss.str());
+ Owner->Status(_("Connected to %s (%s)"), Host.c_str(), Name);
+ _error->Discard();
+ Owner->SetFailReason("");
+ LastUsed = Addr;
return std::move(Fd);
}
- ResultState DoConnect(unsigned long TimeOut);
+ ResultState DoConnect();
+
ResultState CheckError();
};
-ResultState Connection::DoConnect(unsigned long TimeOut)
+ResultState Connection::DoConnect()
{
getnameinfo(Addr->ai_addr,Addr->ai_addrlen,
Name,sizeof(Name),Service,sizeof(Service),
@@ -183,19 +190,7 @@ ResultState Connection::DoConnect(unsigned long TimeOut)
return ResultState::TRANSIENT_ERROR;
}
- /* This implements a timeout for connect by opening the connection
- nonblocking */
- if (WaitFd(Fd->Fd(), true, TimeOut) == false)
- {
- bad_addr.insert(bad_addr.begin(), std::string(Name));
- Owner->SetFailReason("Timeout");
- _error->Error(_("Could not connect to %s:%s (%s), "
- "connection timed out"),
- Host.c_str(), Service, Name);
- return ResultState::TRANSIENT_ERROR;
- }
-
- return CheckError();
+ return ResultState::SUCCESSFUL;
}
ResultState Connection::CheckError()
@@ -271,6 +266,82 @@ static std::vector<struct addrinfo *> OrderAddresses(struct addrinfo *CurHost)
return std::move(allAddrs);
}
/*}}}*/
+// Check for errors and report them /*{{{*/
+static ResultState WaitAndCheckErrors(std::list<Connection> &Conns, std::unique_ptr<MethodFd> &Fd, long TimeoutMsec, bool ReportTimeout)
+{
+ // The last error detected
+ ResultState Result = ResultState::TRANSIENT_ERROR;
+
+ struct timeval tv = {
+ // Split our millisecond timeout into seconds and microseconds
+ .tv_sec = TimeoutMsec / 1000,
+ .tv_usec = (TimeoutMsec % 1000) * 1000,
+ };
+
+ // We will return once we have no more connections, a time out, or
+ // a success.
+ while (!Conns.empty())
+ {
+ fd_set Set;
+ int nfds = -1;
+
+ FD_ZERO(&Set);
+
+ for (auto &Conn : Conns)
+ {
+ int fd = Conn.Fd->Fd();
+ FD_SET(fd, &Set);
+ nfds = std::max(nfds, fd);
+ }
+
+ {
+ int Res;
+ do
+ {
+ Res = select(nfds + 1, 0, &Set, 0, (TimeoutMsec != 0 ? &tv : 0));
+ } while (Res < 0 && errno == EINTR);
+
+ if (Res == 0)
+ {
+ if (ReportTimeout)
+ {
+ for (auto &Conn : Conns)
+ {
+ Conn.Owner->SetFailReason("Timeout");
+ _error->Error(_("Could not connect to %s:%s (%s), "
+ "connection timed out"),
+ Conn.Host.c_str(), Conn.Service, Conn.Name);
+ }
+ }
+ return ResultState::TRANSIENT_ERROR;
+ }
+ }
+
+ // iterate over connections, remove failed ones, and return if
+ // there was a successful one.
+ for (auto ConnI = Conns.begin(); ConnI != Conns.end();)
+ {
+ if (!FD_ISSET(ConnI->Fd->Fd(), &Set))
+ {
+ ConnI++;
+ continue;
+ }
+
+ Result = ConnI->CheckError();
+ if (Result == ResultState::SUCCESSFUL)
+ {
+ Fd = ConnI->Take();
+ return Result;
+ }
+
+ // Connection failed. Erase it and continue to next position
+ ConnI = Conns.erase(ConnI);
+ }
+ }
+
+ return Result;
+}
+ /*}}}*/
// 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,
@@ -375,19 +446,22 @@ static ResultState ConnectToHostname(std::string const &Host, int const Port,
// When we have an IP rotation stay with the last IP.
auto Addresses = OrderAddresses(LastUsed != nullptr ? LastUsed : LastHostAddr);
+ std::list<Connection> Conns;
- for (auto CurHost : Addresses)
+ for (auto Addr : Addresses)
{
- _error->Discard();
- Connection Conn(CurHost, Host, Owner);
- auto const result = Conn.DoConnect(TimeOut);
- if (result == ResultState::SUCCESSFUL)
- {
- Fd = Conn.Take();
- LastUsed = CurHost;
- return result;
- }
- }
+ Connection Conn(Addr, Host, Owner);
+ if (Conn.DoConnect() != ResultState::SUCCESSFUL)
+ continue;
+
+ Conns.push_back(std::move(Conn));
+
+ if (WaitAndCheckErrors(Conns, Fd, Owner->ConfigFindI("ConnectionAttemptDelayMsec", 250), false) == ResultState::SUCCESSFUL)
+ return ResultState::SUCCESSFUL;
+ }
+
+ if (WaitAndCheckErrors(Conns, Fd, TimeOut * 1000, true) == ResultState::SUCCESSFUL)
+ return ResultState::SUCCESSFUL;
if (_error->PendingError() == true)
return ResultState::FATAL_ERROR;