diff options
author | David Kalnischkies <david@kalnischkies.de> | 2017-10-26 00:57:26 +0200 |
---|---|---|
committer | David Kalnischkies <david@kalnischkies.de> | 2017-12-13 23:56:29 +0100 |
commit | 47c0bdc310c8cd62374ca6e6bb456dd183bdfc07 (patch) | |
tree | b951a0221dd6015ffef42ebea9dfc709f6053404 /methods | |
parent | 2f6aed72f656494d668918aa8ce4052d7c81e993 (diff) | |
download | apt-47c0bdc310c8cd62374ca6e6bb456dd183bdfc07.tar.gz |
report transient errors as transient errors
The Fail method for acquire methods has a boolean parameter indicating
the transient-nature of a reported error. The problem with this is that
Fail is called very late at a point where it is no longer easily
identifiable if an error is indeed transient or not, so some calls were
and some weren't and the acquire system would later mostly ignore the
transient flag and guess by using the FailReason instead.
Introducing a tri-state enum we can pass the information about fatal or
transient errors through the callstack to generate the correct fails.
Diffstat (limited to 'methods')
-rw-r--r-- | methods/aptmethod.h | 7 | ||||
-rw-r--r-- | methods/basehttp.cc | 45 | ||||
-rw-r--r-- | methods/basehttp.h | 13 | ||||
-rw-r--r-- | methods/connect.cc | 261 | ||||
-rw-r--r-- | methods/connect.h | 10 | ||||
-rw-r--r-- | methods/ftp.cc | 108 | ||||
-rw-r--r-- | methods/ftp.h | 4 | ||||
-rw-r--r-- | methods/http.cc | 203 | ||||
-rw-r--r-- | methods/http.h | 14 |
9 files changed, 433 insertions, 232 deletions
diff --git a/methods/aptmethod.h b/methods/aptmethod.h index 8d37cbe80..88d325cba 100644 --- a/methods/aptmethod.h +++ b/methods/aptmethod.h @@ -29,6 +29,13 @@ #include <seccomp.h> #endif +enum class ResultState +{ + TRANSIENT_ERROR, + FATAL_ERROR, + SUCCESSFUL +}; + static bool hasDoubleColon(std::string const &n) { return n.find("::") != std::string::npos; diff --git a/methods/basehttp.cc b/methods/basehttp.cc index 3b4c60720..2473ecb5c 100644 --- a/methods/basehttp.cc +++ b/methods/basehttp.cc @@ -74,9 +74,8 @@ ServerState::RunHeadersResult ServerState::RunHeaders(RequestState &Req, Persistent = false; return RUN_HEADERS_OK; - } - while (LoadNextResponse(false, Req) == true); - + } while (LoadNextResponse(false, Req) == ResultState::SUCCESSFUL); + return RUN_HEADERS_IO_ERROR; } /*}}}*/ @@ -598,11 +597,18 @@ int BaseHttpMethod::Loop() QueueBack = Queue; // Connect to the host - if (Server->Open() == false) + switch (Server->Open()) { + case ResultState::FATAL_ERROR: + Fail(false); + Server = nullptr; + continue; + case ResultState::TRANSIENT_ERROR: Fail(true); Server = nullptr; continue; + case ResultState::SUCCESSFUL: + break; } // Fill the pipeline. @@ -657,13 +663,13 @@ int BaseHttpMethod::Loop() URIStart(Res); // Run the data - bool Result = true; + ResultState Result = ResultState::SUCCESSFUL; - // ensure we don't fetch too much - // we could do "Server->MaximumSize = Queue->MaximumSize" here - // but that would break the clever pipeline messup detection - // so instead we use the size of the biggest item in the queue - Req.MaximumSize = FindMaximumObjectSizeInQueue(); + // ensure we don't fetch too much + // we could do "Server->MaximumSize = Queue->MaximumSize" here + // but that would break the clever pipeline messup detection + // so instead we use the size of the biggest item in the queue + Req.MaximumSize = FindMaximumObjectSizeInQueue(); if (Req.HaveContent) { @@ -692,10 +698,10 @@ int BaseHttpMethod::Loop() SetFailReason("MaximumSizeExceeded"); _error->Error(_("File has unexpected size (%llu != %llu). Mirror sync in progress?"), filesize, Queue->ExpectedHashes.FileSize()); - Result = false; + Result = ResultState::FATAL_ERROR; } } - if (Result) + if (Result == ResultState::SUCCESSFUL) Result = Server->RunData(Req); } @@ -715,7 +721,7 @@ int BaseHttpMethod::Loop() utimes(Queue->DestFile.c_str(), times); // Send status to APT - if (Result == true) + if (Result == ResultState::SUCCESSFUL) { Hashes * const resultHashes = Server->GetHashes(); HashStringList const hashList = resultHashes->GetHashStringList(); @@ -768,8 +774,17 @@ int BaseHttpMethod::Loop() else { Server->Close(); - Fail(true); - } + switch (Result) + { + case ResultState::TRANSIENT_ERROR: + Fail(true); + break; + case ResultState::FATAL_ERROR: + case ResultState::SUCCESSFUL: + Fail(false); + break; + } + } } break; } diff --git a/methods/basehttp.h b/methods/basehttp.h index aadd59168..8220c1b3c 100644 --- a/methods/basehttp.h +++ b/methods/basehttp.h @@ -62,7 +62,6 @@ struct RequestState RequestState(BaseHttpMethod * const Owner, ServerState * const Server) : Owner(Owner), Server(Server) { time(&Date); } }; - struct ServerState { bool Persistent; @@ -78,7 +77,7 @@ struct ServerState BaseHttpMethod *Owner; virtual bool ReadHeaderLines(std::string &Data) = 0; - virtual bool LoadNextResponse(bool const ToFile, RequestState &Req) = 0; + virtual ResultState LoadNextResponse(bool const ToFile, RequestState &Req) = 0; public: @@ -99,16 +98,16 @@ struct ServerState virtual bool WriteResponse(std::string const &Data) = 0; /** \brief Transfer the data from the socket */ - virtual bool RunData(RequestState &Req) = 0; - virtual bool RunDataToDevNull(RequestState &Req) = 0; + virtual ResultState RunData(RequestState &Req) = 0; + virtual ResultState RunDataToDevNull(RequestState &Req) = 0; - virtual bool Open() = 0; + virtual ResultState Open() = 0; virtual bool IsOpen() = 0; virtual bool Close() = 0; virtual bool InitHashes(HashStringList const &ExpectedHashes) = 0; - virtual bool Die(RequestState &Req) = 0; + virtual ResultState Die(RequestState &Req) = 0; virtual bool Flush(FileFd * const File) = 0; - virtual bool Go(bool ToFile, RequestState &Req) = 0; + virtual ResultState Go(bool ToFile, RequestState &Req) = 0; virtual Hashes * GetHashes() = 0; ServerState(URI Srv, BaseHttpMethod *Owner); diff --git a/methods/connect.cc b/methods/connect.cc index 6a7b71c0b..1354fe97b 100644 --- a/methods/connect.cc +++ b/methods/connect.cc @@ -112,8 +112,8 @@ std::unique_ptr<MethodFd> MethodFd::FromFd(int iFd) // DoConnect - Attempt a connect operation /*{{{*/ // --------------------------------------------------------------------- /* This helper function attempts a connection to a single address. */ -static bool DoConnect(struct addrinfo *Addr, std::string const &Host, - unsigned long TimeOut, std::unique_ptr<MethodFd> &Fd, aptMethod *Owner) +static ResultState DoConnect(struct addrinfo *Addr, std::string const &Host, + unsigned long TimeOut, std::unique_ptr<MethodFd> &Fd, aptMethod *Owner) { // Show a status indicator char Name[NI_MAXHOST]; @@ -129,7 +129,7 @@ static bool DoConnect(struct addrinfo *Addr, std::string const &Host, // if that addr did timeout before, we do not try it again if(bad_addr.find(std::string(Name)) != bad_addr.end()) - return false; + return ResultState::TRANSIENT_ERROR; /* If this is an IP rotation store the IP we are using.. If something goes wrong this will get tacked onto the end of the error message */ @@ -143,31 +143,43 @@ static bool DoConnect(struct addrinfo *Addr, std::string const &Host, // Get a socket if ((static_cast<FdFd *>(Fd.get())->fd = socket(Addr->ai_family, Addr->ai_socktype, Addr->ai_protocol)) < 0) - return _error->Errno("socket",_("Could not create a socket for %s (f=%u t=%u p=%u)"), - Name,Addr->ai_family,Addr->ai_socktype,Addr->ai_protocol); + { + _error->Errno("socket", _("Could not create a socket for %s (f=%u t=%u p=%u)"), + Name, Addr->ai_family, Addr->ai_socktype, Addr->ai_protocol); + return ResultState::FATAL_ERROR; + } SetNonBlock(Fd->Fd(), true); if (connect(Fd->Fd(), Addr->ai_addr, Addr->ai_addrlen) < 0 && errno != EINPROGRESS) - return _error->Errno("connect",_("Cannot initiate the connection " - "to %s:%s (%s)."),Host.c_str(),Service,Name); - + { + _error->Errno("connect", _("Cannot initiate the connection " + "to %s:%s (%s)."), + Host.c_str(), Service, Name); + 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"); - return _error->Error(_("Could not connect to %s:%s (%s), " - "connection timed out"),Host.c_str(),Service,Name); + _error->Error(_("Could not connect to %s:%s (%s), " + "connection timed out"), + Host.c_str(), Service, Name); + return ResultState::TRANSIENT_ERROR; } // Check the socket for an error condition unsigned int Err; unsigned int Len = sizeof(Err); if (getsockopt(Fd->Fd(), SOL_SOCKET, SO_ERROR, &Err, &Len) != 0) - return _error->Errno("getsockopt",_("Failed")); - + { + _error->Errno("getsockopt", _("Failed")); + return ResultState::FATAL_ERROR; + } + if (Err != 0) { errno = Err; @@ -176,22 +188,23 @@ static bool DoConnect(struct addrinfo *Addr, std::string const &Host, else if (errno == ETIMEDOUT) Owner->SetFailReason("ConnectionTimedOut"); bad_addr.insert(bad_addr.begin(), std::string(Name)); - return _error->Errno("connect",_("Could not connect to %s:%s (%s)."),Host.c_str(), - Service,Name); + _error->Errno("connect", _("Could not connect to %s:%s (%s)."), Host.c_str(), + Service, Name); + return ResultState::TRANSIENT_ERROR; } Owner->SetFailReason(""); - return true; + return ResultState::SUCCESSFUL; } /*}}}*/ // Connect to a given Hostname /*{{{*/ -static bool ConnectToHostname(std::string const &Host, int const Port, - const char *const Service, int DefPort, std::unique_ptr<MethodFd> &Fd, - unsigned long const TimeOut, aptMethod *const Owner) +static ResultState ConnectToHostname(std::string const &Host, int const Port, + const char *const Service, int DefPort, std::unique_ptr<MethodFd> &Fd, + unsigned long const TimeOut, aptMethod *const Owner) { if (ConnectionAllowed(Service, Host) == false) - return false; + return ResultState::FATAL_ERROR; // Convert the port name/number char ServStr[300]; if (Port != 0) @@ -238,8 +251,11 @@ static bool ConnectToHostname(std::string const &Host, int const Port, Hints.ai_family = AF_UNSPEC; // if we couldn't resolve the host before, we don't try now - if(bad_addr.find(Host) != bad_addr.end()) - return _error->Error(_("Could not resolve '%s'"),Host.c_str()); + if (bad_addr.find(Host) != bad_addr.end()) + { + _error->Error(_("Could not resolve '%s'"), Host.c_str()); + return ResultState::TRANSIENT_ERROR; + } // Resolve both the host and service simultaneously while (1) @@ -258,20 +274,24 @@ static bool ConnectToHostname(std::string const &Host, int const Port, } bad_addr.insert(bad_addr.begin(), Host); Owner->SetFailReason("ResolveFailure"); - return _error->Error(_("Could not resolve '%s'"),Host.c_str()); + _error->Error(_("Could not resolve '%s'"), Host.c_str()); + return ResultState::TRANSIENT_ERROR; } if (Res == EAI_AGAIN) { Owner->SetFailReason("TmpResolveFailure"); - return _error->Error(_("Temporary failure resolving '%s'"), - Host.c_str()); + _error->Error(_("Temporary failure resolving '%s'"), + Host.c_str()); + return ResultState::TRANSIENT_ERROR; } if (Res == EAI_SYSTEM) - return _error->Errno("getaddrinfo", _("System error resolving '%s:%s'"), - Host.c_str(),ServStr); - return _error->Error(_("Something wicked happened resolving '%s:%s' (%i - %s)"), - Host.c_str(),ServStr,Res,gai_strerror(Res)); + _error->Errno("getaddrinfo", _("System error resolving '%s:%s'"), + Host.c_str(), ServStr); + else + _error->Error(_("Something wicked happened resolving '%s:%s' (%i - %s)"), + Host.c_str(), ServStr, Res, gai_strerror(Res)); + return ResultState::TRANSIENT_ERROR; } break; } @@ -287,10 +307,11 @@ static bool ConnectToHostname(std::string const &Host, int const Port, while (CurHost != 0) { - if (DoConnect(CurHost,Host,TimeOut,Fd,Owner) == true) + auto const result = DoConnect(CurHost, Host, TimeOut, Fd, Owner); + if (result == ResultState::SUCCESSFUL) { LastUsed = CurHost; - return true; + return result; } Fd->Close(); @@ -315,22 +336,23 @@ static bool ConnectToHostname(std::string const &Host, int const Port, } if (_error->PendingError() == true) - return false; - return _error->Error(_("Unable to connect to %s:%s:"),Host.c_str(),ServStr); + return ResultState::FATAL_ERROR; + _error->Error(_("Unable to connect to %s:%s:"), Host.c_str(), ServStr); + return ResultState::TRANSIENT_ERROR; } /*}}}*/ // Connect - Connect to a server /*{{{*/ // --------------------------------------------------------------------- /* Performs a connection to the server (including SRV record lookup) */ -bool Connect(std::string Host, int Port, const char *Service, - int DefPort, std::unique_ptr<MethodFd> &Fd, - unsigned long TimeOut, aptMethod *Owner) +ResultState Connect(std::string Host, int Port, const char *Service, + int DefPort, std::unique_ptr<MethodFd> &Fd, + unsigned long TimeOut, aptMethod *Owner) { if (_error->PendingError() == true) - return false; + return ResultState::FATAL_ERROR; if (ConnectionAllowed(Service, Host) == false) - return false; + return ResultState::FATAL_ERROR; if(LastHost != Host || LastPort != Port) { @@ -340,8 +362,12 @@ bool Connect(std::string Host, int Port, const char *Service, GetSrvRecords(Host, DefPort, SrvRecords); // RFC2782 defines that a lonely '.' target is an abort reason if (SrvRecords.size() == 1 && SrvRecords[0].target.empty()) - return _error->Error("SRV records for %s indicate that " - "%s service is not available at this domain", Host.c_str(), Service); + { + _error->Error("SRV records for %s indicate that " + "%s service is not available at this domain", + Host.c_str(), Service); + return ResultState::FATAL_ERROR; + } } } @@ -358,11 +384,11 @@ bool Connect(std::string Host, int Port, const char *Service, Host = Srv.target; Port = Srv.port; auto const ret = ConnectToHostname(Host, Port, Service, DefPort, Fd, TimeOut, Owner); - if (ret) + if (ret == ResultState::SUCCESSFUL) { while(stackSize--) _error->RevertToStack(); - return true; + return ret; } } Host = std::move(initialHost); @@ -374,7 +400,7 @@ bool Connect(std::string Host, int Port, const char *Service, auto const ret = ConnectToHostname(Host, Port, Service, DefPort, Fd, TimeOut, Owner); while(stackSize--) - if (ret) + if (ret == ResultState::SUCCESSFUL) _error->RevertToStack(); else _error->MergeWithStack(); @@ -403,8 +429,8 @@ static bool TalkToSocksProxy(int const ServerFd, std::string const &Proxy, return true; } -bool UnwrapSocks(std::string Host, int Port, URI Proxy, std::unique_ptr<MethodFd> &Fd, - unsigned long Timeout, aptMethod *Owner) +ResultState UnwrapSocks(std::string Host, int Port, URI Proxy, std::unique_ptr<MethodFd> &Fd, + unsigned long Timeout, aptMethod *Owner) { /* We implement a very basic SOCKS5 client here complying mostly to RFC1928 expect * for not offering GSSAPI auth which is a must (we only do no or user/pass auth). @@ -413,14 +439,20 @@ bool UnwrapSocks(std::string Host, int Port, URI Proxy, std::unique_ptr<MethodFd Owner->Status(_("Connecting to %s (%s)"), "SOCKS5h proxy", ProxyInfo.c_str()); #define APT_WriteOrFail(TYPE, DATA, LENGTH) \ if (TalkToSocksProxy(Fd->Fd(), ProxyInfo, TYPE, true, DATA, LENGTH, Timeout) == false) \ - return false + return ResultState::TRANSIENT_ERROR #define APT_ReadOrFail(TYPE, DATA, LENGTH) \ if (TalkToSocksProxy(Fd->Fd(), ProxyInfo, TYPE, false, DATA, LENGTH, Timeout) == false) \ - return false + return ResultState::TRANSIENT_ERROR if (Host.length() > 255) - return _error->Error("Can't use SOCKS5h as hostname %s is too long!", Host.c_str()); + { + _error->Error("Can't use SOCKS5h as hostname %s is too long!", Host.c_str()); + return ResultState::FATAL_ERROR; + } if (Proxy.User.length() > 255 || Proxy.Password.length() > 255) - return _error->Error("Can't use user&pass auth as they are too long (%lu and %lu) for the SOCKS5!", Proxy.User.length(), Proxy.Password.length()); + { + _error->Error("Can't use user&pass auth as they are too long (%lu and %lu) for the SOCKS5!", Proxy.User.length(), Proxy.Password.length()); + return ResultState::FATAL_ERROR; + } if (Proxy.User.empty()) { uint8_t greeting[] = {0x05, 0x01, 0x00}; @@ -434,13 +466,19 @@ bool UnwrapSocks(std::string Host, int Port, URI Proxy, std::unique_ptr<MethodFd uint8_t greeting[2]; APT_ReadOrFail("greet back", greeting, sizeof(greeting)); if (greeting[0] != 0x05) - return _error->Error("SOCKS proxy %s greets back with wrong version: %d", ProxyInfo.c_str(), greeting[0]); + { + _error->Error("SOCKS proxy %s greets back with wrong version: %d", ProxyInfo.c_str(), greeting[0]); + return ResultState::FATAL_ERROR; + } if (greeting[1] == 0x00) ; // no auth has no method-dependent sub-negotiations else if (greeting[1] == 0x02) { if (Proxy.User.empty()) - return _error->Error("SOCKS proxy %s negotiated user&pass auth, but we had not offered it!", ProxyInfo.c_str()); + { + _error->Error("SOCKS proxy %s negotiated user&pass auth, but we had not offered it!", ProxyInfo.c_str()); + return ResultState::FATAL_ERROR; + } // user&pass auth sub-negotiations are defined by RFC1929 std::vector<uint8_t> auth = {{0x01, static_cast<uint8_t>(Proxy.User.length())}}; std::copy(Proxy.User.begin(), Proxy.User.end(), std::back_inserter(auth)); @@ -450,12 +488,21 @@ bool UnwrapSocks(std::string Host, int Port, URI Proxy, std::unique_ptr<MethodFd uint8_t authstatus[2]; APT_ReadOrFail("auth report", authstatus, sizeof(authstatus)); if (authstatus[0] != 0x01) - return _error->Error("SOCKS proxy %s auth status response with wrong version: %d", ProxyInfo.c_str(), authstatus[0]); + { + _error->Error("SOCKS proxy %s auth status response with wrong version: %d", ProxyInfo.c_str(), authstatus[0]); + return ResultState::FATAL_ERROR; + } if (authstatus[1] != 0x00) - return _error->Error("SOCKS proxy %s reported authorization failure: username or password incorrect? (%d)", ProxyInfo.c_str(), authstatus[1]); + { + _error->Error("SOCKS proxy %s reported authorization failure: username or password incorrect? (%d)", ProxyInfo.c_str(), authstatus[1]); + return ResultState::FATAL_ERROR; + } } else - return _error->Error("SOCKS proxy %s greets back having not found a common authorization method: %d", ProxyInfo.c_str(), greeting[1]); + { + _error->Error("SOCKS proxy %s greets back having not found a common authorization method: %d", ProxyInfo.c_str(), greeting[1]); + return ResultState::FATAL_ERROR; + } union { uint16_t *i; uint8_t *b; @@ -470,9 +517,15 @@ bool UnwrapSocks(std::string Host, int Port, URI Proxy, std::unique_ptr<MethodFd uint8_t response[4]; APT_ReadOrFail("first part of response", response, sizeof(response)); if (response[0] != 0x05) - return _error->Error("SOCKS proxy %s response with wrong version: %d", ProxyInfo.c_str(), response[0]); + { + _error->Error("SOCKS proxy %s response with wrong version: %d", ProxyInfo.c_str(), response[0]); + return ResultState::FATAL_ERROR; + } if (response[2] != 0x00) - return _error->Error("SOCKS proxy %s has unexpected non-zero reserved field value: %d", ProxyInfo.c_str(), response[2]); + { + _error->Error("SOCKS proxy %s has unexpected non-zero reserved field value: %d", ProxyInfo.c_str(), response[2]); + return ResultState::FATAL_ERROR; + } std::string bindaddr; if (response[3] == 0x01) // IPv4 address { @@ -508,12 +561,16 @@ bool UnwrapSocks(std::string Host, int Port, URI Proxy, std::unique_ptr<MethodFd port); } else - return _error->Error("SOCKS proxy %s destination address is of unknown type: %d", - ProxyInfo.c_str(), response[3]); + { + _error->Error("SOCKS proxy %s destination address is of unknown type: %d", + ProxyInfo.c_str(), response[3]); + return ResultState::FATAL_ERROR; + } if (response[1] != 0x00) { char const *errstr = nullptr; auto errcode = response[1]; + bool Transient = false; // Tor error reporting can be a bit arcane, lets try to detect & fix it up if (bindaddr == "0.0.0.0:0") { @@ -554,18 +611,22 @@ bool UnwrapSocks(std::string Host, int Port, URI Proxy, std::unique_ptr<MethodFd case 0x03: errstr = "Network unreachable"; Owner->SetFailReason("ConnectionTimedOut"); + Transient = true; break; case 0x04: errstr = "Host unreachable"; Owner->SetFailReason("ConnectionTimedOut"); + Transient = true; break; case 0x05: errstr = "Connection refused"; Owner->SetFailReason("ConnectionRefused"); + Transient = true; break; case 0x06: errstr = "TTL expired"; Owner->SetFailReason("Timeout"); + Transient = true; break; case 0x07: errstr = "Command not supported"; @@ -581,20 +642,24 @@ bool UnwrapSocks(std::string Host, int Port, URI Proxy, std::unique_ptr<MethodFd break; } } - return _error->Error("SOCKS proxy %s could not connect to %s (%s) due to: %s (%d)", - ProxyInfo.c_str(), Host.c_str(), bindaddr.c_str(), errstr, response[1]); + _error->Error("SOCKS proxy %s could not connect to %s (%s) due to: %s (%d)", + ProxyInfo.c_str(), Host.c_str(), bindaddr.c_str(), errstr, response[1]); + return Transient ? ResultState::TRANSIENT_ERROR : ResultState::FATAL_ERROR; } else if (Owner->DebugEnabled()) ioprintf(std::clog, "http: SOCKS proxy %s connection established to %s (%s)\n", ProxyInfo.c_str(), Host.c_str(), bindaddr.c_str()); if (WaitFd(Fd->Fd(), true, Timeout) == false) - return _error->Error("SOCKS proxy %s reported connection to %s (%s), but timed out", - ProxyInfo.c_str(), Host.c_str(), bindaddr.c_str()); + { + _error->Error("SOCKS proxy %s reported connection to %s (%s), but timed out", + ProxyInfo.c_str(), Host.c_str(), bindaddr.c_str()); + return ResultState::TRANSIENT_ERROR; + } #undef APT_ReadOrFail #undef APT_WriteOrFail - return true; + return ResultState::SUCCESSFUL; } /*}}}*/ // UnwrapTLS - Handle TLS connections /*{{{*/ @@ -643,11 +708,14 @@ struct TlsFd : public MethodFd } }; -bool UnwrapTLS(std::string Host, std::unique_ptr<MethodFd> &Fd, - unsigned long Timeout, aptMethod *Owner) +ResultState UnwrapTLS(std::string Host, std::unique_ptr<MethodFd> &Fd, + unsigned long Timeout, aptMethod *Owner) { if (_config->FindB("Acquire::AllowTLS", true) == false) - return _error->Error("TLS support has been disabled: Acquire::AllowTLS is false."); + { + _error->Error("TLS support has been disabled: Acquire::AllowTLS is false."); + return ResultState::FATAL_ERROR; + } int err; TlsFd *tlsFd = new TlsFd(); @@ -656,7 +724,10 @@ bool UnwrapTLS(std::string Host, std::unique_ptr<MethodFd> &Fd, tlsFd->UnderlyingFd = MethodFd::FromFd(-1); // For now if ((err = gnutls_init(&tlsFd->session, GNUTLS_CLIENT | GNUTLS_NONBLOCK)) < 0) - return _error->Error("Internal error: could not allocate credentials: %s", gnutls_strerror(err)); + { + _error->Error("Internal error: could not allocate credentials: %s", gnutls_strerror(err)); + return ResultState::FATAL_ERROR; + } FdFd *fdfd = dynamic_cast<FdFd *>(Fd.get()); if (fdfd != nullptr) @@ -677,7 +748,10 @@ bool UnwrapTLS(std::string Host, std::unique_ptr<MethodFd> &Fd, } if ((err = gnutls_certificate_allocate_credentials(&tlsFd->credentials)) < 0) - return _error->Error("Internal error: could not allocate credentials: %s", gnutls_strerror(err)); + { + _error->Error("Internal error: could not allocate credentials: %s", gnutls_strerror(err)); + return ResultState::FATAL_ERROR; + } // Credential setup std::string fileinfo = Owner->ConfigFind("CaInfo", ""); @@ -688,7 +762,10 @@ bool UnwrapTLS(std::string Host, std::unique_ptr<MethodFd> &Fd, if (err == 0) Owner->Warning("No system certificates available. Try installing ca-certificates."); else if (err < 0) - return _error->Error("Could not load system TLS certificates: %s", gnutls_strerror(err)); + { + _error->Error("Could not load system TLS certificates: %s", gnutls_strerror(err)); + return ResultState::FATAL_ERROR; + } } else { @@ -696,13 +773,22 @@ bool UnwrapTLS(std::string Host, std::unique_ptr<MethodFd> &Fd, gnutls_certificate_set_verify_flags(tlsFd->credentials, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT); err = gnutls_certificate_set_x509_trust_file(tlsFd->credentials, fileinfo.c_str(), GNUTLS_X509_FMT_PEM); if (err < 0) - return _error->Error("Could not load certificates from %s (CaInfo option): %s", fileinfo.c_str(), gnutls_strerror(err)); + { + _error->Error("Could not load certificates from %s (CaInfo option): %s", fileinfo.c_str(), gnutls_strerror(err)); + return ResultState::FATAL_ERROR; + } } if (!Owner->ConfigFind("IssuerCert", "").empty()) - return _error->Error("The option '%s' is not supported anymore", "IssuerCert"); + { + _error->Error("The option '%s' is not supported anymore", "IssuerCert"); + return ResultState::FATAL_ERROR; + } if (!Owner->ConfigFind("SslForceVersion", "").empty()) - return _error->Error("The option '%s' is not supported anymore", "SslForceVersion"); + { + _error->Error("The option '%s' is not supported anymore", "SslForceVersion"); + return ResultState::FATAL_ERROR; + } // For client authentication, certificate file ... std::string const cert = Owner->ConfigFind("SslCert", ""); @@ -715,7 +801,8 @@ bool UnwrapTLS(std::string Host, std::unique_ptr<MethodFd> &Fd, key.empty() ? cert.c_str() : key.c_str(), GNUTLS_X509_FMT_PEM)) < 0) { - return _error->Error("Could not load client certificate (%s, SslCert option) or key (%s, SslKey option): %s", cert.c_str(), key.c_str(), gnutls_strerror(err)); + _error->Error("Could not load client certificate (%s, SslCert option) or key (%s, SslKey option): %s", cert.c_str(), key.c_str(), gnutls_strerror(err)); + return ResultState::FATAL_ERROR; } } @@ -726,14 +813,23 @@ bool UnwrapTLS(std::string Host, std::unique_ptr<MethodFd> &Fd, if ((err = gnutls_certificate_set_x509_crl_file(tlsFd->credentials, crlfile.c_str(), GNUTLS_X509_FMT_PEM)) < 0) - return _error->Error("Could not load custom certificate revocation list %s (CrlFile option): %s", crlfile.c_str(), gnutls_strerror(err)); + { + _error->Error("Could not load custom certificate revocation list %s (CrlFile option): %s", crlfile.c_str(), gnutls_strerror(err)); + return ResultState::FATAL_ERROR; + } } if ((err = gnutls_credentials_set(tlsFd->session, GNUTLS_CRD_CERTIFICATE, tlsFd->credentials)) < 0) - return _error->Error("Internal error: Could not add certificates to session: %s", gnutls_strerror(err)); + { + _error->Error("Internal error: Could not add certificates to session: %s", gnutls_strerror(err)); + return ResultState::FATAL_ERROR; + } if ((err = gnutls_set_default_priority(tlsFd->session)) < 0) - return _error->Error("Internal error: Could not set algorithm preferences: %s", gnutls_strerror(err)); + { + _error->Error("Internal error: Could not set algorithm preferences: %s", gnutls_strerror(err)); + return ResultState::FATAL_ERROR; + } if (Owner->ConfigFindB("Verify-Peer", true)) { @@ -749,7 +845,10 @@ bool UnwrapTLS(std::string Host, std::unique_ptr<MethodFd> &Fd, inet_pton(AF_INET6, tlsFd->hostname.c_str(), &addr6) == 1) /* not a host name */; else if ((err = gnutls_server_name_set(tlsFd->session, GNUTLS_NAME_DNS, tlsFd->hostname.c_str(), tlsFd->hostname.length())) < 0) - return _error->Error("Could not set host name %s to indicate to server: %s", tlsFd->hostname.c_str(), gnutls_strerror(err)); + { + _error->Error("Could not set host name %s to indicate to server: %s", tlsFd->hostname.c_str(), gnutls_strerror(err)); + return ResultState::FATAL_ERROR; + } } // Set the FD now, so closing it works reliably. @@ -763,7 +862,10 @@ bool UnwrapTLS(std::string Host, std::unique_ptr<MethodFd> &Fd, err = gnutls_handshake(tlsFd->session); if ((err == GNUTLS_E_INTERRUPTED || err == GNUTLS_E_AGAIN) && WaitFd(Fd->Fd(), gnutls_record_get_direction(tlsFd->session) == 1, Timeout) == false) - return _error->Errno("select", "Could not wait for server fd"); + { + _error->Errno("select", "Could not wait for server fd"); + return ResultState::TRANSIENT_ERROR; + } } while (err < 0 && gnutls_error_is_fatal(err) == 0); if (err < 0) @@ -781,9 +883,10 @@ bool UnwrapTLS(std::string Host, std::unique_ptr<MethodFd> &Fd, } gnutls_free(txt.data); } - return _error->Error("Could not handshake: %s", gnutls_strerror(err)); + _error->Error("Could not handshake: %s", gnutls_strerror(err)); + return ResultState::FATAL_ERROR; } - return true; + return ResultState::SUCCESSFUL; } /*}}}*/ diff --git a/methods/connect.h b/methods/connect.h index 817ebf765..63a94400e 100644 --- a/methods/connect.h +++ b/methods/connect.h @@ -14,7 +14,7 @@ #include <string> #include <stddef.h> -class aptMethod; +#include "aptmethod.h" /** * \brief Small representation of a file descriptor for network traffic. @@ -39,11 +39,11 @@ struct MethodFd virtual bool HasPending(); }; -bool Connect(std::string To, int Port, const char *Service, int DefPort, - std::unique_ptr<MethodFd> &Fd, unsigned long TimeOut, aptMethod *Owner); +ResultState Connect(std::string To, int Port, const char *Service, int DefPort, + std::unique_ptr<MethodFd> &Fd, unsigned long TimeOut, aptMethod *Owner); -bool UnwrapSocks(std::string To, int Port, URI Proxy, std::unique_ptr<MethodFd> &Fd, unsigned long Timeout, aptMethod *Owner); -bool UnwrapTLS(std::string To, std::unique_ptr<MethodFd> &Fd, unsigned long Timeout, aptMethod *Owner); +ResultState UnwrapSocks(std::string To, int Port, URI Proxy, std::unique_ptr<MethodFd> &Fd, unsigned long Timeout, aptMethod *Owner); +ResultState UnwrapTLS(std::string To, std::unique_ptr<MethodFd> &Fd, unsigned long Timeout, aptMethod *Owner); void RotateDNS(); diff --git a/methods/ftp.cc b/methods/ftp.cc index 99eebc5af..2daba8ffe 100644 --- a/methods/ftp.cc +++ b/methods/ftp.cc @@ -110,12 +110,12 @@ void FTPConn::Close() // --------------------------------------------------------------------- /* Connect to the server using a non-blocking connection and perform a login. */ -bool FTPConn::Open(aptMethod *Owner) +ResultState FTPConn::Open(aptMethod *Owner) { // Use the already open connection if possible. if (ServerFd->Fd() != -1) - return true; - + return ResultState::SUCCESSFUL; + Close(); // Determine the proxy setting @@ -167,31 +167,40 @@ bool FTPConn::Open(aptMethod *Owner) /* Connect to the remote server. Since FTP is connection oriented we want to make sure we get a new server every time we reconnect */ RotateDNS(); - if (Connect(Host,Port,"ftp",21,ServerFd,TimeOut,Owner) == false) - return false; + auto result = Connect(Host, Port, "ftp", 21, ServerFd, TimeOut, Owner); + if (result != ResultState::SUCCESSFUL) + return result; // Login must be before getpeername otherwise dante won't work. Owner->Status(_("Logging in")); - bool Res = Login(); - + result = Login(); + if (result != ResultState::SUCCESSFUL) + return result; + // Get the remote server's address PeerAddrLen = sizeof(PeerAddr); if (getpeername(ServerFd->Fd(), (sockaddr *)&PeerAddr, &PeerAddrLen) != 0) - return _error->Errno("getpeername",_("Unable to determine the peer name")); - + { + _error->Errno("getpeername", _("Unable to determine the peer name")); + return ResultState::TRANSIENT_ERROR; + } + // Get the local machine's address ServerAddrLen = sizeof(ServerAddr); if (getsockname(ServerFd->Fd(), (sockaddr *)&ServerAddr, &ServerAddrLen) != 0) - return _error->Errno("getsockname",_("Unable to determine the local name")); - - return Res; + { + _error->Errno("getsockname", _("Unable to determine the local name")); + return ResultState::TRANSIENT_ERROR; + } + + return ResultState::SUCCESSFUL; } /*}}}*/ // FTPConn::Login - Login to the remote server /*{{{*/ // --------------------------------------------------------------------- /* This performs both normal login and proxy login using a simples script stored in the config file. */ -bool FTPConn::Login() +ResultState FTPConn::Login() { unsigned int Tag; string Msg; @@ -211,22 +220,31 @@ bool FTPConn::Login() { // Read the initial response if (ReadResp(Tag,Msg) == false) - return false; + return ResultState::TRANSIENT_ERROR; if (Tag >= 400) - return _error->Error(_("The server refused the connection and said: %s"),Msg.c_str()); - + { + _error->Error(_("The server refused the connection and said: %s"), Msg.c_str()); + return ResultState::FATAL_ERROR; + } + // Send the user if (WriteMsg(Tag,Msg,"USER %s",User.c_str()) == false) - return false; + return ResultState::TRANSIENT_ERROR; if (Tag >= 400) - return _error->Error(_("USER failed, server said: %s"),Msg.c_str()); - + { + _error->Error(_("USER failed, server said: %s"), Msg.c_str()); + return ResultState::FATAL_ERROR; + } + if (Tag == 331) { // 331 User name okay, need password. // Send the Password if (WriteMsg(Tag,Msg,"PASS %s",Pass.c_str()) == false) - return false; - if (Tag >= 400) - return _error->Error(_("PASS failed, server said: %s"),Msg.c_str()); + return ResultState::TRANSIENT_ERROR; + if (Tag >= 400) + { + _error->Error(_("PASS failed, server said: %s"), Msg.c_str()); + return ResultState::FATAL_ERROR; + } } // Enter passive mode @@ -239,15 +257,21 @@ bool FTPConn::Login() { // Read the initial response if (ReadResp(Tag,Msg) == false) - return false; + return ResultState::TRANSIENT_ERROR; if (Tag >= 400) - return _error->Error(_("The server refused the connection and said: %s"),Msg.c_str()); - + { + _error->Error(_("The server refused the connection and said: %s"), Msg.c_str()); + return ResultState::TRANSIENT_ERROR; + } + // Perform proxy script execution Configuration::Item const *Opts = _config->Tree("Acquire::ftp::ProxyLogin"); if (Opts == 0 || Opts->Child == 0) - return _error->Error(_("A proxy server was specified but no login " - "script, Acquire::ftp::ProxyLogin is empty.")); + { + _error->Error(_("A proxy server was specified but no login " + "script, Acquire::ftp::ProxyLogin is empty.")); + return ResultState::FATAL_ERROR; + } Opts = Opts->Child; // Iterate over the entire login script @@ -274,9 +298,12 @@ bool FTPConn::Login() // Send the command if (WriteMsg(Tag,Msg,"%s",Tmp.c_str()) == false) - return false; + return ResultState::TRANSIENT_ERROR; if (Tag >= 400) - return _error->Error(_("Login script command '%s' failed, server said: %s"),Tmp.c_str(),Msg.c_str()); + { + _error->Error(_("Login script command '%s' failed, server said: %s"), Tmp.c_str(), Msg.c_str()); + return ResultState::FATAL_ERROR; + } } // Enter passive mode @@ -300,11 +327,13 @@ bool FTPConn::Login() // Binary mode if (WriteMsg(Tag,Msg,"TYPE I") == false) - return false; + return ResultState::TRANSIENT_ERROR; if (Tag >= 400) - return _error->Error(_("TYPE failed, server said: %s"),Msg.c_str()); - - return true; + { + _error->Error(_("TYPE failed, server said: %s"), Msg.c_str()); + return ResultState::FATAL_ERROR; + } + return ResultState::SUCCESSFUL; } /*}}}*/ // FTPConn::ReadLine - Read a line from the server /*{{{*/ @@ -1023,15 +1052,22 @@ bool FtpMethod::Fetch(FetchItem *Itm) delete Server; Server = new FTPConn(Get); } - + // Could not connect is a transient error.. - if (Server->Open(this) == false) + switch (Server->Open(this)) { + case ResultState::TRANSIENT_ERROR: Server->Close(); Fail(true); return true; + case ResultState::FATAL_ERROR: + Server->Close(); + Fail(false); + return true; + case ResultState::SUCCESSFUL: + break; } - + // Get the files information Status(_("Query")); unsigned long long Size; diff --git a/methods/ftp.h b/methods/ftp.h index 1859ddce0..52b856637 100644 --- a/methods/ftp.h +++ b/methods/ftp.h @@ -43,7 +43,7 @@ class FTPConn // Private helper functions bool ReadLine(std::string &Text); - bool Login(); + ResultState Login(); bool CreateDataFd(); bool Finalize(); @@ -56,7 +56,7 @@ class FTPConn bool WriteMsg(unsigned int &Ret,std::string &Text,const char *Fmt,...); // Connection control - bool Open(aptMethod *Owner); + ResultState Open(aptMethod *Owner); void Close(); bool GoPasv(); bool ExtGoPasv(); diff --git a/methods/http.cc b/methods/http.cc index b7c9fe349..2d23b1646 100644 --- a/methods/http.cc +++ b/methods/http.cc @@ -328,8 +328,8 @@ struct HttpConnectFd : public MethodFd } }; -bool UnwrapHTTPConnect(std::string Host, int Port, URI Proxy, std::unique_ptr<MethodFd> &Fd, - unsigned long Timeout, aptAuthConfMethod *Owner) +static ResultState UnwrapHTTPConnect(std::string Host, int Port, URI Proxy, std::unique_ptr<MethodFd> &Fd, + unsigned long Timeout, aptAuthConfMethod *Owner) { Owner->Status(_("Connecting to %s (%s)"), "HTTP proxy", URI::SiteOnly(Proxy).c_str()); // The HTTP server expects a hostname with a trailing :port @@ -369,17 +369,29 @@ bool UnwrapHTTPConnect(std::string Host, int Port, URI Proxy, std::unique_ptr<Me while (Out.WriteSpace() > 0) { if (WaitFd(Fd->Fd(), true, Timeout) == false) - return _error->Errno("select", "Writing to proxy failed"); + { + _error->Errno("select", "Writing to proxy failed"); + return ResultState::TRANSIENT_ERROR; + } if (Out.Write(Fd) == false) - return _error->Errno("write", "Writing to proxy failed"); + { + _error->Errno("write", "Writing to proxy failed"); + return ResultState::TRANSIENT_ERROR; + } } while (In.ReadSpace() > 0) { if (WaitFd(Fd->Fd(), false, Timeout) == false) - return _error->Errno("select", "Reading from proxy failed"); + { + _error->Errno("select", "Reading from proxy failed"); + return ResultState::TRANSIENT_ERROR; + } if (In.Read(Fd) == false) - return _error->Errno("read", "Reading from proxy failed"); + { + _error->Errno("read", "Reading from proxy failed"); + return ResultState::TRANSIENT_ERROR; + } if (In.WriteTillEl(Headers)) break; @@ -389,7 +401,10 @@ bool UnwrapHTTPConnect(std::string Host, int Port, URI Proxy, std::unique_ptr<Me cerr << Headers << endl; if (!(APT::String::Startswith(Headers, "HTTP/1.0 200") || APT::String::Startswith(Headers, "HTTP/1.1 200"))) - return _error->Error("Invalid response from proxy: %s", Headers.c_str()); + { + _error->Error("Invalid response from proxy: %s", Headers.c_str()); + return ResultState::TRANSIENT_ERROR; + } if (In.WriteSpace() > 0) { @@ -400,7 +415,7 @@ bool UnwrapHTTPConnect(std::string Host, int Port, URI Proxy, std::unique_ptr<Me Fd = std::move(NewFd); } - return true; + return ResultState::SUCCESSFUL; } /*}}}*/ @@ -415,12 +430,12 @@ HttpServerState::HttpServerState(URI Srv,HttpMethod *Owner) : ServerState(Srv, O // HttpServerState::Open - Open a connection to the server /*{{{*/ // --------------------------------------------------------------------- /* This opens a connection to the server. */ -bool HttpServerState::Open() +ResultState HttpServerState::Open() { // Use the already open connection if possible. if (ServerFd->Fd() != -1) - return true; - + return ResultState::SUCCESSFUL; + Close(); In.Reset(); Out.Reset(); @@ -476,12 +491,14 @@ bool HttpServerState::Open() auto const DefaultPort = tls ? 443 : 80; if (Proxy.Access == "socks5h") { - if (Connect(Proxy.Host, Proxy.Port, "socks", 1080, ServerFd, TimeOut, Owner) == false) - return false; - - if (UnwrapSocks(ServerName.Host, ServerName.Port == 0 ? DefaultPort : ServerName.Port, - Proxy, ServerFd, Owner->ConfigFindI("TimeOut", 120), Owner) == false) - return false; + auto result = Connect(Proxy.Host, Proxy.Port, "socks", 1080, ServerFd, TimeOut, Owner); + if (result != ResultState::SUCCESSFUL) + return result; + + result = UnwrapSocks(ServerName.Host, ServerName.Port == 0 ? DefaultPort : ServerName.Port, + Proxy, ServerFd, Owner->ConfigFindI("TimeOut", 120), Owner); + if (result != ResultState::SUCCESSFUL) + return result; } else { @@ -495,7 +512,10 @@ bool HttpServerState::Open() Host = ServerName.Host; } else if (Proxy.Access != "http" && Proxy.Access != "https") - return _error->Error("Unsupported proxy configured: %s", URI::SiteOnly(Proxy).c_str()); + { + _error->Error("Unsupported proxy configured: %s", URI::SiteOnly(Proxy).c_str()); + return ResultState::FATAL_ERROR; + } else { if (Proxy.Port != 0) @@ -505,18 +525,27 @@ bool HttpServerState::Open() if (Proxy.Access == "https" && Port == 0) Port = 443; } - if (!Connect(Host, Port, DefaultService, DefaultPort, ServerFd, TimeOut, Owner)) - return false; - if (Host == Proxy.Host && Proxy.Access == "https" && UnwrapTLS(Proxy.Host, ServerFd, TimeOut, Owner) == false) - return false; - if (Host == Proxy.Host && tls && UnwrapHTTPConnect(ServerName.Host, ServerName.Port == 0 ? DefaultPort : ServerName.Port, Proxy, ServerFd, Owner->ConfigFindI("TimeOut", 120), Owner) == false) - return false; + auto result = Connect(Host, Port, DefaultService, DefaultPort, ServerFd, TimeOut, Owner); + if (result != ResultState::SUCCESSFUL) + return result; + if (Host == Proxy.Host && Proxy.Access == "https") + { + result = UnwrapTLS(Proxy.Host, ServerFd, TimeOut, Owner); + if (result != ResultState::SUCCESSFUL) + return result; + } + if (Host == Proxy.Host && tls) + { + result = UnwrapHTTPConnect(ServerName.Host, ServerName.Port == 0 ? DefaultPort : ServerName.Port, Proxy, ServerFd, Owner->ConfigFindI("TimeOut", 120), Owner); + if (result != ResultState::SUCCESSFUL) + return result; + } } - if (tls && UnwrapTLS(ServerName.Host, ServerFd, TimeOut, Owner) == false) - return false; + if (tls) + return UnwrapTLS(ServerName.Host, ServerFd, TimeOut, Owner); - return true; + return ResultState::SUCCESSFUL; } /*}}}*/ // HttpServerState::Close - Close a connection to the server /*{{{*/ @@ -529,7 +558,7 @@ bool HttpServerState::Close() } /*}}}*/ // HttpServerState::RunData - Transfer the data from the socket /*{{{*/ -bool HttpServerState::RunData(RequestState &Req) +ResultState HttpServerState::RunData(RequestState &Req) { Req.State = RequestState::Data; @@ -539,19 +568,18 @@ bool HttpServerState::RunData(RequestState &Req) while (1) { // Grab the block size - bool Last = true; + ResultState Last = ResultState::SUCCESSFUL; string Data; In.Limit(-1); do { if (In.WriteTillEl(Data,true) == true) break; - } - while ((Last = Go(false, Req)) == true); + } while ((Last = Go(false, Req)) == ResultState::SUCCESSFUL); + + if (Last != ResultState::SUCCESSFUL) + return Last; - if (Last == false) - return false; - // See if we are done unsigned long long Len = strtoull(Data.c_str(),0,16); if (Len == 0) @@ -559,39 +587,35 @@ bool HttpServerState::RunData(RequestState &Req) In.Limit(-1); // We have to remove the entity trailer - Last = true; + Last = ResultState::SUCCESSFUL; do { if (In.WriteTillEl(Data,true) == true && Data.length() <= 2) break; - } - while ((Last = Go(false, Req)) == true); - if (Last == false) - return false; - return !_error->PendingError(); + } while ((Last = Go(false, Req)) == ResultState::SUCCESSFUL); + return Last; } - + // Transfer the block In.Limit(Len); - while (Go(true, Req) == true) + while (Go(true, Req) == ResultState::SUCCESSFUL) if (In.IsLimit() == true) break; // Error if (In.IsLimit() == false) - return false; - + return ResultState::TRANSIENT_ERROR; + // The server sends an extra new line before the next block specifier.. In.Limit(-1); - Last = true; + Last = ResultState::SUCCESSFUL; do { if (In.WriteTillEl(Data,true) == true) break; - } - while ((Last = Go(false, Req)) == true); - if (Last == false) - return false; + } while ((Last = Go(false, Req)) == ResultState::SUCCESSFUL); + if (Last != ResultState::SUCCESSFUL) + return Last; } } else @@ -605,34 +629,41 @@ bool HttpServerState::RunData(RequestState &Req) if (Req.MaximumSize != 0 && Req.DownloadSize > Req.MaximumSize) { Owner->SetFailReason("MaximumSizeExceeded"); - return _error->Error(_("File has unexpected size (%llu != %llu). Mirror sync in progress?"), - Req.DownloadSize, Req.MaximumSize); + _error->Error(_("File has unexpected size (%llu != %llu). Mirror sync in progress?"), + Req.DownloadSize, Req.MaximumSize); + return ResultState::FATAL_ERROR; } In.Limit(Req.DownloadSize); } else if (Persistent == false) In.Limit(-1); - + // Just transfer the whole block. - do + while (true) { if (In.IsLimit() == false) - continue; - + { + auto const result = Go(true, Req); + if (result == ResultState::SUCCESSFUL) + continue; + return result; + } + In.Limit(-1); - return !_error->PendingError(); + return _error->PendingError() ? ResultState::FATAL_ERROR : ResultState::SUCCESSFUL; } - while (Go(true, Req) == true); } - return Flush(&Req.File) && !_error->PendingError(); + if (Flush(&Req.File) == false) + return ResultState::TRANSIENT_ERROR; + return ResultState::SUCCESSFUL; } /*}}}*/ -bool HttpServerState::RunDataToDevNull(RequestState &Req) /*{{{*/ +ResultState HttpServerState::RunDataToDevNull(RequestState &Req) /*{{{*/ { // no need to clean up if we discard the connection anyhow if (Persistent == false) - return true; + return ResultState::SUCCESSFUL; Req.File.Open("/dev/null", FileFd::WriteOnly); return RunData(Req); } @@ -642,7 +673,7 @@ bool HttpServerState::ReadHeaderLines(std::string &Data) /*{{{*/ return In.WriteTillEl(Data); } /*}}}*/ -bool HttpServerState::LoadNextResponse(bool const ToFile, RequestState &Req)/*{{{*/ +ResultState HttpServerState::LoadNextResponse(bool const ToFile, RequestState &Req) /*{{{*/ { return Go(ToFile, Req); } @@ -677,7 +708,7 @@ APT_PURE Hashes * HttpServerState::GetHashes() /*{{{*/ } /*}}}*/ // HttpServerState::Die - The server has closed the connection. /*{{{*/ -bool HttpServerState::Die(RequestState &Req) +ResultState HttpServerState::Die(RequestState &Req) { unsigned int LErrno = errno; @@ -685,7 +716,7 @@ bool HttpServerState::Die(RequestState &Req) if (Req.State == RequestState::Data) { if (Req.File.IsOpen() == false) - return true; + return ResultState::SUCCESSFUL; // on GNU/kFreeBSD, apt dies on /dev/null because non-blocking // can't be set if (Req.File.Name() != "/dev/null") @@ -693,11 +724,14 @@ bool HttpServerState::Die(RequestState &Req) while (In.WriteSpace() == true) { if (In.Write(MethodFd::FromFd(Req.File.Fd())) == false) - return _error->Errno("write",_("Error writing to the file")); + { + _error->Errno("write", _("Error writing to the file")); + return ResultState::TRANSIENT_ERROR; + } // Done if (In.IsLimit() == true) - return true; + return ResultState::SUCCESSFUL; } } @@ -707,9 +741,13 @@ bool HttpServerState::Die(RequestState &Req) { Close(); if (LErrno == 0) - return _error->Error(_("Error reading from server. Remote end closed connection")); + { + _error->Error(_("Error reading from server. Remote end closed connection")); + return ResultState::TRANSIENT_ERROR; + } errno = LErrno; - return _error->Errno("read",_("Error reading from server")); + _error->Errno("read", _("Error reading from server")); + return ResultState::TRANSIENT_ERROR; } else { @@ -717,14 +755,14 @@ bool HttpServerState::Die(RequestState &Req) // Nothing left in the buffer if (In.WriteSpace() == false) - return false; + return ResultState::TRANSIENT_ERROR; // We may have got multiple responses back in one packet.. Close(); - return true; + return ResultState::SUCCESSFUL; } - return false; + return ResultState::TRANSIENT_ERROR; } /*}}}*/ // HttpServerState::Flush - Dump the buffer into the file /*{{{*/ @@ -760,12 +798,12 @@ bool HttpServerState::Flush(FileFd * const File) // --------------------------------------------------------------------- /* This runs the select loop over the server FDs, Output file FDs and stdin. */ -bool HttpServerState::Go(bool ToFile, RequestState &Req) +ResultState HttpServerState::Go(bool ToFile, RequestState &Req) { // Server has closed the connection if (ServerFd->Fd() == -1 && (In.WriteSpace() == false || ToFile == false)) - return false; + return ResultState::TRANSIENT_ERROR; // Handle server IO if (ServerFd->HasPending() && In.ReadSpace() == true) @@ -811,8 +849,9 @@ bool HttpServerState::Go(bool ToFile, RequestState &Req) if ((Res = select(MaxFd+1,&rfds,&wfds,0,&tv)) < 0) { if (errno == EINTR) - return true; - return _error->Errno("select",_("Select failed")); + return ResultState::SUCCESSFUL; + _error->Errno("select", _("Select failed")); + return ResultState::TRANSIENT_ERROR; } if (Res == 0) @@ -840,14 +879,18 @@ bool HttpServerState::Go(bool ToFile, RequestState &Req) if (FileFD->Fd() != -1 && FD_ISSET(FileFD->Fd(), &wfds)) { if (In.Write(FileFD) == false) - return _error->Errno("write",_("Error writing to output file")); + { + _error->Errno("write", _("Error writing to output file")); + return ResultState::TRANSIENT_ERROR; + } } if (Req.MaximumSize > 0 && Req.File.IsOpen() && Req.File.Failed() == false && Req.File.Tell() > Req.MaximumSize) { Owner->SetFailReason("MaximumSizeExceeded"); - return _error->Error(_("File has unexpected size (%llu != %llu). Mirror sync in progress?"), - Req.File.Tell(), Req.MaximumSize); + _error->Error(_("File has unexpected size (%llu != %llu). Mirror sync in progress?"), + Req.File.Tell(), Req.MaximumSize); + return ResultState::FATAL_ERROR; } // Handle commands from APT @@ -855,9 +898,9 @@ bool HttpServerState::Go(bool ToFile, RequestState &Req) { if (Owner->Run(true) != -1) exit(100); - } - - return true; + } + + return ResultState::SUCCESSFUL; } /*}}}*/ diff --git a/methods/http.h b/methods/http.h index 6d44fbdd4..84cc0b2b1 100644 --- a/methods/http.h +++ b/methods/http.h @@ -93,8 +93,6 @@ class CircleBuf ~CircleBuf(); }; -bool UnwrapHTTPConnect(std::string To, int Port, URI Proxy, std::unique_ptr<MethodFd> &Fd, unsigned long Timeout, aptAuthConfMethod *Owner); - struct HttpServerState: public ServerState { // This is the connection itself. Output is data FROM the server @@ -104,23 +102,23 @@ struct HttpServerState: public ServerState protected: virtual bool ReadHeaderLines(std::string &Data) APT_OVERRIDE; - virtual bool LoadNextResponse(bool const ToFile, RequestState &Req) APT_OVERRIDE; + virtual ResultState LoadNextResponse(bool const ToFile, RequestState &Req) APT_OVERRIDE; virtual bool WriteResponse(std::string const &Data) APT_OVERRIDE; public: virtual void Reset() APT_OVERRIDE; - virtual bool RunData(RequestState &Req) APT_OVERRIDE; - virtual bool RunDataToDevNull(RequestState &Req) APT_OVERRIDE; + virtual ResultState RunData(RequestState &Req) APT_OVERRIDE; + virtual ResultState RunDataToDevNull(RequestState &Req) APT_OVERRIDE; - virtual bool Open() APT_OVERRIDE; + virtual ResultState Open() APT_OVERRIDE; virtual bool IsOpen() APT_OVERRIDE; virtual bool Close() APT_OVERRIDE; virtual bool InitHashes(HashStringList const &ExpectedHashes) APT_OVERRIDE; virtual Hashes * GetHashes() APT_OVERRIDE; - virtual bool Die(RequestState &Req) APT_OVERRIDE; + virtual ResultState Die(RequestState &Req) APT_OVERRIDE; virtual bool Flush(FileFd * const File) APT_OVERRIDE; - virtual bool Go(bool ToFile, RequestState &Req) APT_OVERRIDE; + virtual ResultState Go(bool ToFile, RequestState &Req) APT_OVERRIDE; HttpServerState(URI Srv, HttpMethod *Owner); virtual ~HttpServerState() {Close();}; |