diff options
-rw-r--r-- | methods/aptmethod.h | 72 | ||||
-rw-r--r-- | methods/cdrom.cc | 4 | ||||
-rw-r--r-- | methods/gpgv.cc | 6 | ||||
-rw-r--r-- | methods/http.cc | 59 | ||||
-rw-r--r-- | methods/http.h | 10 | ||||
-rw-r--r-- | methods/http_main.cc | 8 | ||||
-rw-r--r-- | methods/https.cc | 226 | ||||
-rw-r--r-- | methods/https.h | 13 | ||||
-rw-r--r-- | methods/mirror.cc | 4 | ||||
-rw-r--r-- | methods/rred.cc | 2 | ||||
-rw-r--r-- | methods/rsh.cc | 12 | ||||
-rw-r--r-- | methods/rsh.h | 3 | ||||
-rw-r--r-- | methods/server.cc | 57 | ||||
-rw-r--r-- | methods/server.h | 2 | ||||
-rw-r--r-- | methods/store.cc | 11 | ||||
-rwxr-xr-x | test/integration/test-bug-738785-switch-protocol | 2 | ||||
-rwxr-xr-x | test/integration/test-different-methods-for-same-source | 15 | ||||
-rwxr-xr-x | test/integration/test-pdiff-usage | 2 |
18 files changed, 303 insertions, 205 deletions
diff --git a/methods/aptmethod.h b/methods/aptmethod.h index 0963ea21e..38f451708 100644 --- a/methods/aptmethod.h +++ b/methods/aptmethod.h @@ -5,8 +5,10 @@ #include <apt-pkg/configuration.h> #include <apt-pkg/error.h> +#include <algorithm> #include <locale> #include <string> +#include <vector> #include <sys/time.h> #include <sys/types.h> @@ -15,10 +17,15 @@ #include <apti18n.h> +static bool hasDoubleColon(std::string const &n) +{ + return n.find("::") != std::string::npos; +} + class aptMethod : public pkgAcqMethod { protected: - std::string Binary; + std::string const Binary; public: virtual bool Configuration(std::string Message) APT_OVERRIDE @@ -34,7 +41,7 @@ public: return true; } - bool CalculateHashes(FetchItem const * const Itm, FetchResult &Res) const + bool CalculateHashes(FetchItem const * const Itm, FetchResult &Res) const APT_NONNULL(2) { Hashes Hash(Itm->ExpectedHashes); FileFd Fd; @@ -52,7 +59,62 @@ public: va_end(args); } - bool TransferModificationTimes(char const * const From, char const * const To, time_t &LastModified) + std::vector<std::string> methodNames; + void setPostfixForMethodNames(char const * const postfix) APT_NONNULL(2) + { + methodNames.erase(std::remove_if(methodNames.begin(), methodNames.end(), hasDoubleColon), methodNames.end()); + decltype(methodNames) toAdd; + for (auto && name: methodNames) + toAdd.emplace_back(name + "::" + postfix); + std::move(toAdd.begin(), toAdd.end(), std::back_inserter(methodNames)); + } + bool DebugEnabled() const + { + if (methodNames.empty()) + return false; + auto const sni = std::find_if_not(methodNames.crbegin(), methodNames.crend(), hasDoubleColon); + if (unlikely(sni == methodNames.crend())) + return false; + auto const ln = methodNames[methodNames.size() - 1]; + // worst case: all three are the same + std::string confln, confsn, confpn; + strprintf(confln, "Debug::Acquire::%s", ln.c_str()); + strprintf(confsn, "Debug::Acquire::%s", sni->c_str()); + auto const pni = sni->substr(0, sni->find('+')); + strprintf(confpn, "Debug::Acquire::%s", pni.c_str()); + return _config->FindB(confln,_config->FindB(confsn, _config->FindB(confpn, false))); + } + std::string ConfigFind(char const * const postfix, std::string const &defValue) const APT_NONNULL(2) + { + for (auto && name: methodNames) + { + std::string conf; + strprintf(conf, "Acquire::%s::%s", name.c_str(), postfix); + auto const value = _config->Find(conf); + if (value.empty() == false) + return value; + } + return defValue; + } + std::string ConfigFind(std::string const &postfix, std::string const &defValue) const + { + return ConfigFind(postfix.c_str(), defValue); + } + bool ConfigFindB(char const * const postfix, bool const defValue) const APT_NONNULL(2) + { + return StringToBool(ConfigFind(postfix, defValue ? "yes" : "no"), defValue); + } + int ConfigFindI(char const * const postfix, int const defValue) const APT_NONNULL(2) + { + char *End; + std::string const value = ConfigFind(postfix, ""); + auto const Res = strtol(value.c_str(), &End, 0); + if (value.c_str() == End) + return defValue; + return Res; + } + + bool TransferModificationTimes(char const * const From, char const * const To, time_t &LastModified) APT_NONNULL(2, 3) { if (strcmp(To, "/dev/null") == 0) return true; @@ -75,8 +137,8 @@ public: return true; } - aptMethod(char const * const Binary, char const * const Ver, unsigned long const Flags) : - pkgAcqMethod(Ver, Flags), Binary(Binary) + aptMethod(std::string &&Binary, char const * const Ver, unsigned long const Flags) APT_NONNULL(3) : + pkgAcqMethod(Ver, Flags), Binary(Binary), methodNames({Binary}) { try { std::locale::global(std::locale("")); diff --git a/methods/cdrom.cc b/methods/cdrom.cc index 161822ac6..87a58e948 100644 --- a/methods/cdrom.cc +++ b/methods/cdrom.cc @@ -177,7 +177,7 @@ bool CDROMMethod::Fetch(FetchItem *Itm) URI Get = Itm->Uri; string File = Get.Path; - Debug = _config->FindB("Debug::Acquire::cdrom", false); + Debug = DebugEnabled(); if (Debug) clog << "CDROMMethod::Fetch " << Itm->Uri << endl; @@ -224,7 +224,7 @@ bool CDROMMethod::Fetch(FetchItem *Itm) return true; } - bool AutoDetect = _config->FindB("Acquire::cdrom::AutoDetect", true); + bool const AutoDetect = ConfigFindB("AutoDetect", true); CDROM = _config->FindDir("Acquire::cdrom::mount"); if (Debug) clog << "Looking for CDROM at " << CDROM << endl; diff --git a/methods/gpgv.cc b/methods/gpgv.cc index fc6eb9159..2fed53a39 100644 --- a/methods/gpgv.cc +++ b/methods/gpgv.cc @@ -147,7 +147,7 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, vector<Signer> &SoonWorthlessSigners, vector<string> &NoPubKeySigners) { - bool const Debug = _config->FindB("Debug::Acquire::gpgv", false); + bool const Debug = DebugEnabled(); if (Debug == true) std::clog << "inside VerifyGetSigners" << std::endl; @@ -415,10 +415,8 @@ bool GPGVMethod::URIAcquire(std::string const &Message, FetchItem *Itm) std::move(NoPubKeySigners.begin(), NoPubKeySigners.end(), std::back_inserter(Res.GPGVOutput)); URIDone(Res); - if (_config->FindB("Debug::Acquire::gpgv", false)) - { + if (DebugEnabled()) std::clog << "apt-key succeeded\n"; - } return true; } diff --git a/methods/http.cc b/methods/http.cc index b7a3aa4a1..0378d7c60 100644 --- a/methods/http.cc +++ b/methods/http.cc @@ -63,13 +63,13 @@ const unsigned int CircleBuf::BW_HZ=10; // CircleBuf::CircleBuf - Circular input buffer /*{{{*/ // --------------------------------------------------------------------- /* */ -CircleBuf::CircleBuf(unsigned long long Size) +CircleBuf::CircleBuf(HttpMethod const * const Owner, unsigned long long Size) : Size(Size), Hash(NULL), TotalWriten(0) { Buf = new unsigned char[Size]; Reset(); - CircleBuf::BwReadLimit = _config->FindI("Acquire::http::Dl-Limit",0)*1024; + CircleBuf::BwReadLimit = Owner->ConfigFindI("Dl-Limit", 0) * 1024; } /*}}}*/ // CircleBuf::Reset - Reset to the default state /*{{{*/ @@ -287,9 +287,9 @@ CircleBuf::~CircleBuf() } // HttpServerState::HttpServerState - Constructor /*{{{*/ -HttpServerState::HttpServerState(URI Srv,HttpMethod *Owner) : ServerState(Srv, Owner), In(64*1024), Out(4*1024) +HttpServerState::HttpServerState(URI Srv,HttpMethod *Owner) : ServerState(Srv, Owner), In(Owner, 64*1024), Out(Owner, 4*1024) { - TimeOut = _config->FindI("Acquire::http::Timeout",TimeOut); + TimeOut = Owner->ConfigFindI("Timeout", TimeOut); Reset(); } /*}}}*/ @@ -309,7 +309,7 @@ bool HttpServerState::Open() // Determine the proxy setting AutoDetectProxy(ServerName); - string SpecificProxy = _config->Find("Acquire::http::Proxy::" + ServerName.Host); + string SpecificProxy = Owner->ConfigFind("Proxy::" + ServerName.Host, ""); if (!SpecificProxy.empty()) { if (SpecificProxy == "DIRECT") @@ -319,7 +319,7 @@ bool HttpServerState::Open() } else { - string DefProxy = _config->Find("Acquire::http::Proxy"); + string DefProxy = Owner->ConfigFind("Proxy", ""); if (!DefProxy.empty()) { Proxy = DefProxy; @@ -616,7 +616,7 @@ bool HttpServerState::Go(bool ToFile, FileFd * const File) FD_SET(FileFD,&wfds); // Add stdin - if (_config->FindB("Acquire::http::DependOnSTDIN", true) == true) + if (Owner->ConfigFindB("DependOnSTDIN", true) == true) FD_SET(STDIN_FILENO,&rfds); // Figure out the max fd @@ -688,6 +688,11 @@ bool HttpServerState::Go(bool ToFile, FileFd * const File) void HttpMethod::SendReq(FetchItem *Itm) { URI Uri = Itm->Uri; + { + auto const plus = Binary.find('+'); + if (plus != std::string::npos) + Uri.Access = Binary.substr(plus + 1); + } // The HTTP server expects a hostname with a trailing :port std::stringstream Req; @@ -705,7 +710,7 @@ void HttpMethod::SendReq(FetchItem *Itm) if (Server->Proxy.empty() == true || Server->Proxy.Host.empty()) requesturi = Uri.Path; else - requesturi = Itm->Uri; + requesturi = Uri; // The "+" is encoded as a workaround for a amazon S3 bug // see LP bugs #1003633 and #1086997. @@ -722,12 +727,12 @@ void HttpMethod::SendReq(FetchItem *Itm) Req << "Host: " << ProperHost << "\r\n"; // generate a cache control header (if needed) - if (_config->FindB("Acquire::http::No-Cache",false) == true) + if (ConfigFindB("No-Cache",false) == true) Req << "Cache-Control: no-cache\r\n" << "Pragma: no-cache\r\n"; else if (Itm->IndexFile == true) - Req << "Cache-Control: max-age=" << std::to_string(_config->FindI("Acquire::http::Max-Age",0)) << "\r\n"; - else if (_config->FindB("Acquire::http::No-Store",false) == true) + Req << "Cache-Control: max-age=" << std::to_string(ConfigFindI("Max-Age", 0)) << "\r\n"; + else if (ConfigFindB("No-Store", false) == true) Req << "Cache-Control: no-store\r\n"; // If we ask for uncompressed files servers might respond with content- @@ -735,7 +740,7 @@ void HttpMethod::SendReq(FetchItem *Itm) // see 657029, 657560 and co, so if we have no extension on the request // ask for text only. As a sidenote: If there is nothing to negotate servers // seem to be nice and ignore it. - if (_config->FindB("Acquire::http::SendAccept", true) == true) + if (ConfigFindB("SendAccept", true) == true) { size_t const filepos = Itm->Uri.find_last_of('/'); string const file = Itm->Uri.substr(filepos + 1); @@ -760,7 +765,7 @@ void HttpMethod::SendReq(FetchItem *Itm) Req << "Authorization: Basic " << Base64Encode(Uri.User + ":" + Uri.Password) << "\r\n"; - Req << "User-Agent: " << _config->Find("Acquire::http::User-Agent", + Req << "User-Agent: " << ConfigFind("User-Agent", "Debian APT-HTTP/1.3 (" PACKAGE_VERSION ")") << "\r\n"; Req << "\r\n"; @@ -771,22 +776,6 @@ void HttpMethod::SendReq(FetchItem *Itm) Server->WriteResponse(Req.str()); } /*}}}*/ -// HttpMethod::Configuration - Handle a configuration message /*{{{*/ -// --------------------------------------------------------------------- -/* We stash the desired pipeline depth */ -bool HttpMethod::Configuration(string Message) -{ - if (ServerMethod::Configuration(Message) == false) - return false; - - AllowRedirect = _config->FindB("Acquire::http::AllowRedirect",true); - PipelineDepth = _config->FindI("Acquire::http::Pipeline-Depth", - PipelineDepth); - Debug = _config->FindB("Debug::Acquire::http",false); - - return true; -} - /*}}}*/ std::unique_ptr<ServerState> HttpMethod::CreateServerState(URI const &uri)/*{{{*/ { return std::unique_ptr<ServerState>(new HttpServerState(uri, this)); @@ -826,3 +815,15 @@ ServerMethod::DealWithHeadersResult HttpMethod::DealWithHeaders(FetchResult &Res return FILE_IS_OPEN; } /*}}}*/ +HttpMethod::HttpMethod(std::string &&pProg) : ServerMethod(pProg.c_str(), "1.2", Pipeline | SendConfig)/*{{{*/ +{ + auto addName = std::inserter(methodNames, methodNames.begin()); + if (Binary != "http") + addName = "http"; + auto const plus = Binary.find('+'); + if (plus != std::string::npos) + addName = Binary.substr(0, plus); + File = 0; + Server = 0; +} + /*}}}*/ diff --git a/methods/http.h b/methods/http.h index e6b11d642..4d22c88f9 100644 --- a/methods/http.h +++ b/methods/http.h @@ -86,7 +86,7 @@ class CircleBuf // Dump everything void Stats(); - explicit CircleBuf(unsigned long long Size); + CircleBuf(HttpMethod const * const Owner, unsigned long long Size); ~CircleBuf(); }; @@ -126,8 +126,6 @@ class HttpMethod : public ServerMethod public: virtual void SendReq(FetchItem *Itm) APT_OVERRIDE; - virtual bool Configuration(std::string Message) APT_OVERRIDE; - virtual std::unique_ptr<ServerState> CreateServerState(URI const &uri) APT_OVERRIDE; virtual void RotateDNS() APT_OVERRIDE; virtual DealWithHeadersResult DealWithHeaders(FetchResult &Res) APT_OVERRIDE; @@ -138,11 +136,7 @@ class HttpMethod : public ServerMethod public: friend struct HttpServerState; - HttpMethod() : ServerMethod("http", "1.2",Pipeline | SendConfig) - { - File = 0; - Server = 0; - }; + explicit HttpMethod(std::string &&pProg); }; #endif diff --git a/methods/http_main.cc b/methods/http_main.cc index fa183ddb3..1e56044b7 100644 --- a/methods/http_main.cc +++ b/methods/http_main.cc @@ -5,11 +5,13 @@ #include "http.h" -int main() +int main(int, const char *argv[]) { // ignore SIGPIPE, this can happen on write() if the socket // closes the connection (this is dealt with via ServerDie()) signal(SIGPIPE, SIG_IGN); - - return HttpMethod().Loop(); + std::string Binary = flNotDir(argv[0]); + if (Binary.find('+') == std::string::npos && Binary != "http") + Binary.append("+http"); + return HttpMethod(std::move(Binary)).Loop(); } diff --git a/methods/https.cc b/methods/https.cc index a5a8a7cd1..47dce2ea0 100644 --- a/methods/https.cc +++ b/methods/https.cc @@ -25,11 +25,14 @@ #include <sys/time.h> #include <unistd.h> #include <stdio.h> -#include <iostream> -#include <sstream> #include <ctype.h> #include <stdlib.h> +#include <array> +#include <iostream> +#include <sstream> + + #include "https.h" #include <apti18n.h> @@ -148,7 +151,7 @@ HttpsMethod::write_data(void *buffer, size_t size, size_t nmemb, void *userp) // HttpsServerState::HttpsServerState - Constructor /*{{{*/ HttpsServerState::HttpsServerState(URI Srv,HttpsMethod * Owner) : ServerState(Srv, Owner), Hash(NULL) { - TimeOut = _config->FindI("Acquire::https::Timeout",TimeOut); + TimeOut = Owner->ConfigFindI("Timeout", TimeOut); Reset(); } /*}}}*/ @@ -179,32 +182,31 @@ bool HttpsMethod::SetupProxy() /*{{{*/ curl_easy_setopt(curl, CURLOPT_PROXY, ""); // Determine the proxy setting - try https first, fallback to http and use env at last - string UseProxy = _config->Find("Acquire::https::Proxy::" + ServerName.Host, - _config->Find("Acquire::http::Proxy::" + ServerName.Host).c_str()); - + string UseProxy = ConfigFind("Proxy::" + ServerName.Host, ""); if (UseProxy.empty() == true) - UseProxy = _config->Find("Acquire::https::Proxy", _config->Find("Acquire::http::Proxy").c_str()); - - // User want to use NO proxy, so nothing to setup + UseProxy = ConfigFind("Proxy", ""); + // User wants to use NO proxy, so nothing to setup if (UseProxy == "DIRECT") return true; - // Parse no_proxy, a comma (,) separated list of domains we don't want to use + // Parse no_proxy, a comma (,) separated list of domains we don't want to use // a proxy for so we stop right here if it is in the list if (getenv("no_proxy") != 0 && CheckDomainList(ServerName.Host,getenv("no_proxy")) == true) return true; if (UseProxy.empty() == true) { - const char* result = getenv("https_proxy"); + const char* result = nullptr; + if (std::find(methodNames.begin(), methodNames.end(), "https") != methodNames.end()) + result = getenv("https_proxy"); // FIXME: Fall back to http_proxy is to remain compatible with // existing setups and behaviour of apt.conf. This should be // deprecated in the future (including apt.conf). Most other // programs do not fall back to http proxy settings and neither // should Apt. - if (result == NULL) - result = getenv("http_proxy"); - UseProxy = result == NULL ? "" : result; + if (result == nullptr && std::find(methodNames.begin(), methodNames.end(), "http") != methodNames.end()) + result = getenv("http_proxy"); + UseProxy = result == nullptr ? "" : result; } // Determine what host and port to use based on the proxy settings @@ -245,12 +247,19 @@ bool HttpsMethod::Fetch(FetchItem *Itm) struct curl_slist *headers=NULL; char curl_errorstr[CURL_ERROR_SIZE]; URI Uri = Itm->Uri; - string remotehost = Uri.Host; + setPostfixForMethodNames(Uri.Host.c_str()); + AllowRedirect = ConfigFindB("AllowRedirect", true); + Debug = DebugEnabled(); // TODO: // - http::Pipeline-Depth // - error checking/reporting // - more debug options? (CURLOPT_DEBUGFUNCTION?) + { + auto const plus = Binary.find('+'); + if (plus != std::string::npos) + Uri.Access = Binary.substr(plus + 1); + } curl_easy_reset(curl); if (SetupProxy() == false) @@ -270,107 +279,81 @@ bool HttpsMethod::Fetch(FetchItem *Itm) curl_easy_setopt(curl, CURLOPT_NOPROGRESS, true); curl_easy_setopt(curl, CURLOPT_FILETIME, true); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0); - // only allow curl to handle https, not the other stuff it supports - curl_easy_setopt(curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS); - curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS); - - // SSL parameters are set by default to the common (non mirror-specific) value - // if available (or a default one) and gets overload by mirror-specific ones. - - // File containing the list of trusted CA. - string cainfo = _config->Find("Acquire::https::CaInfo",""); - string knob = "Acquire::https::"+remotehost+"::CaInfo"; - cainfo = _config->Find(knob.c_str(),cainfo.c_str()); - if(cainfo.empty() == false) - curl_easy_setopt(curl, CURLOPT_CAINFO,cainfo.c_str()); - - // Check server certificate against previous CA list ... - bool peer_verify = _config->FindB("Acquire::https::Verify-Peer",true); - knob = "Acquire::https::" + remotehost + "::Verify-Peer"; - peer_verify = _config->FindB(knob.c_str(), peer_verify); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, peer_verify); - - // ... and hostname against cert CN or subjectAltName - bool verify = _config->FindB("Acquire::https::Verify-Host",true); - knob = "Acquire::https::"+remotehost+"::Verify-Host"; - verify = _config->FindB(knob.c_str(),verify); - int const default_verify = (verify == true) ? 2 : 0; - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, default_verify); - - // Also enforce issuer of server certificate using its cert - string issuercert = _config->Find("Acquire::https::IssuerCert",""); - knob = "Acquire::https::"+remotehost+"::IssuerCert"; - issuercert = _config->Find(knob.c_str(),issuercert.c_str()); - if(issuercert.empty() == false) - curl_easy_setopt(curl, CURLOPT_ISSUERCERT,issuercert.c_str()); - - // For client authentication, certificate file ... - string pem = _config->Find("Acquire::https::SslCert",""); - knob = "Acquire::https::"+remotehost+"::SslCert"; - pem = _config->Find(knob.c_str(),pem.c_str()); - if(pem.empty() == false) - curl_easy_setopt(curl, CURLOPT_SSLCERT, pem.c_str()); - - // ... and associated key. - string key = _config->Find("Acquire::https::SslKey",""); - knob = "Acquire::https::"+remotehost+"::SslKey"; - key = _config->Find(knob.c_str(),key.c_str()); - if(key.empty() == false) - curl_easy_setopt(curl, CURLOPT_SSLKEY, key.c_str()); - - // Allow forcing SSL version to SSLv3 or TLSv1 (SSLv2 is not - // supported by GnuTLS). - long final_version = CURL_SSLVERSION_DEFAULT; - string sslversion = _config->Find("Acquire::https::SslForceVersion",""); - knob = "Acquire::https::"+remotehost+"::SslForceVersion"; - sslversion = _config->Find(knob.c_str(),sslversion.c_str()); - if(sslversion == "TLSv1") - final_version = CURL_SSLVERSION_TLSv1; - else if(sslversion == "SSLv3") - final_version = CURL_SSLVERSION_SSLv3; - curl_easy_setopt(curl, CURLOPT_SSLVERSION, final_version); - - // CRL file - string crlfile = _config->Find("Acquire::https::CrlFile",""); - knob = "Acquire::https::"+remotehost+"::CrlFile"; - crlfile = _config->Find(knob.c_str(),crlfile.c_str()); - if(crlfile.empty() == false) - curl_easy_setopt(curl, CURLOPT_CRLFILE, crlfile.c_str()); + if (std::find(methodNames.begin(), methodNames.end(), "https") != methodNames.end()) + { + curl_easy_setopt(curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS); + curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS); + + // File containing the list of trusted CA. + std::string const cainfo = ConfigFind("CaInfo", ""); + if(cainfo.empty() == false) + curl_easy_setopt(curl, CURLOPT_CAINFO, cainfo.c_str()); + // Check server certificate against previous CA list ... + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, ConfigFindB("Verify-Peer", true) ? 1 : 0); + // ... and hostname against cert CN or subjectAltName + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, ConfigFindB("Verify-Host", true) ? 2 : 0); + // Also enforce issuer of server certificate using its cert + std::string const issuercert = ConfigFind("IssuerCert", ""); + if(issuercert.empty() == false) + curl_easy_setopt(curl, CURLOPT_ISSUERCERT, issuercert.c_str()); + // For client authentication, certificate file ... + std::string const pem = ConfigFind("SslCert", ""); + if(pem.empty() == false) + curl_easy_setopt(curl, CURLOPT_SSLCERT, pem.c_str()); + // ... and associated key. + std::string const key = ConfigFind("SslKey", ""); + if(key.empty() == false) + curl_easy_setopt(curl, CURLOPT_SSLKEY, key.c_str()); + // Allow forcing SSL version to SSLv3 or TLSv1 + long final_version = CURL_SSLVERSION_DEFAULT; + std::string const sslversion = ConfigFind("SslForceVersion", ""); + if(sslversion == "TLSv1") + final_version = CURL_SSLVERSION_TLSv1; + else if(sslversion == "TLSv1.0") + final_version = CURL_SSLVERSION_TLSv1_0; + else if(sslversion == "TLSv1.1") + final_version = CURL_SSLVERSION_TLSv1_1; + else if(sslversion == "TLSv1.2") + final_version = CURL_SSLVERSION_TLSv1_2; + else if(sslversion == "SSLv3") + final_version = CURL_SSLVERSION_SSLv3; + curl_easy_setopt(curl, CURLOPT_SSLVERSION, final_version); + // CRL file + std::string const crlfile = ConfigFind("CrlFile", ""); + if(crlfile.empty() == false) + curl_easy_setopt(curl, CURLOPT_CRLFILE, crlfile.c_str()); + } + else + { + curl_easy_setopt(curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP); + curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP); + } // cache-control - if(_config->FindB("Acquire::https::No-Cache", - _config->FindB("Acquire::http::No-Cache",false)) == false) + if(ConfigFindB("No-Cache", false) == false) { // cache enabled - if (_config->FindB("Acquire::https::No-Store", - _config->FindB("Acquire::http::No-Store",false)) == true) + if (ConfigFindB("No-Store", false) == true) headers = curl_slist_append(headers,"Cache-Control: no-store"); - stringstream ss; - ioprintf(ss, "Cache-Control: max-age=%u", _config->FindI("Acquire::https::Max-Age", - _config->FindI("Acquire::http::Max-Age",0))); - headers = curl_slist_append(headers, ss.str().c_str()); + std::string ss; + strprintf(ss, "Cache-Control: max-age=%u", ConfigFindI("Max-Age", 0)); + headers = curl_slist_append(headers, ss.c_str()); } else { // cache disabled by user headers = curl_slist_append(headers, "Cache-Control: no-cache"); headers = curl_slist_append(headers, "Pragma: no-cache"); } curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); - // speed limit - int const dlLimit = _config->FindI("Acquire::https::Dl-Limit", - _config->FindI("Acquire::http::Dl-Limit",0))*1024; + int const dlLimit = ConfigFindI("Dl-Limit", 0) * 1024; if (dlLimit > 0) curl_easy_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE, dlLimit); // set header - curl_easy_setopt(curl, CURLOPT_USERAGENT, - _config->Find("Acquire::https::User-Agent", - _config->Find("Acquire::http::User-Agent", - "Debian APT-CURL/1.0 (" PACKAGE_VERSION ")").c_str()).c_str()); + curl_easy_setopt(curl, CURLOPT_USERAGENT, ConfigFind("User-Agent", "Debian APT-CURL/1.0 (" PACKAGE_VERSION ")").c_str()); // set timeout - int const timeout = _config->FindI("Acquire::https::Timeout", - _config->FindI("Acquire::http::Timeout",120)); + int const timeout = ConfigFindI("Timeout", 120); curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, timeout); //set really low lowspeed timeout (see #497983) curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, DL_MIN_SPEED); @@ -389,7 +372,7 @@ bool HttpsMethod::Fetch(FetchItem *Itm) // see 657029, 657560 and co, so if we have no extension on the request // ask for text only. As a sidenote: If there is nothing to negotate servers // seem to be nice and ignore it. - if (_config->FindB("Acquire::https::SendAccept", _config->FindB("Acquire::http::SendAccept", true)) == true) + if (ConfigFindB("SendAccept", true)) { size_t const filepos = Itm->Uri.find_last_of('/'); string const file = Itm->Uri.substr(filepos + 1); @@ -513,27 +496,40 @@ bool HttpsMethod::Fetch(FetchItem *Itm) return true; } /*}}}*/ -// HttpsMethod::Configuration - Handle a configuration message /*{{{*/ -bool HttpsMethod::Configuration(string Message) +std::unique_ptr<ServerState> HttpsMethod::CreateServerState(URI const &uri)/*{{{*/ { - if (ServerMethod::Configuration(Message) == false) - return false; - - AllowRedirect = _config->FindB("Acquire::https::AllowRedirect", - _config->FindB("Acquire::http::AllowRedirect", true)); - Debug = _config->FindB("Debug::Acquire::https",false); - - return true; + return std::unique_ptr<ServerState>(new HttpsServerState(uri, this)); } /*}}}*/ -std::unique_ptr<ServerState> HttpsMethod::CreateServerState(URI const &uri)/*{{{*/ +HttpsMethod::HttpsMethod(std::string &&pProg) : ServerMethod(std::move(pProg),"1.2",Pipeline | SendConfig)/*{{{*/ { - return std::unique_ptr<ServerState>(new HttpsServerState(uri, this)); + auto addName = std::inserter(methodNames, methodNames.begin()); + addName = "http"; + auto const plus = Binary.find('+'); + if (plus != std::string::npos) + { + addName = Binary.substr(plus + 1); + auto base = Binary.substr(0, plus); + if (base != "https") + addName = base; + } + if (std::find(methodNames.begin(), methodNames.end(), "https") != methodNames.end()) + curl_global_init(CURL_GLOBAL_SSL); + else + curl_global_init(CURL_GLOBAL_NOTHING); + curl = curl_easy_init(); } /*}}}*/ - -int main() +HttpsMethod::~HttpsMethod() /*{{{*/ { - return HttpsMethod().Run(); + curl_easy_cleanup(curl); } - + /*}}}*/ +int main(int, const char *argv[]) /*{{{*/ +{ + std::string Binary = flNotDir(argv[0]); + if (Binary.find('+') == std::string::npos && Binary != "https") + Binary.append("+https"); + return HttpsMethod(std::move(Binary)).Run(); +} + /*}}}*/ diff --git a/methods/https.h b/methods/https.h index 85cc7824e..04e72e815 100644 --- a/methods/https.h +++ b/methods/https.h @@ -74,21 +74,12 @@ class HttpsMethod : public ServerMethod public: - virtual bool Configuration(std::string Message) APT_OVERRIDE; virtual std::unique_ptr<ServerState> CreateServerState(URI const &uri) APT_OVERRIDE; using pkgAcqMethod::FetchResult; using pkgAcqMethod::FetchItem; - HttpsMethod() : ServerMethod("https","1.2",Pipeline | SendConfig) - { - curl_global_init(CURL_GLOBAL_SSL); - curl = curl_easy_init(); - }; - - ~HttpsMethod() - { - curl_easy_cleanup(curl); - }; + explicit HttpsMethod(std::string &&pProg); + virtual ~HttpsMethod(); }; #include <apt-pkg/strutl.h> diff --git a/methods/mirror.cc b/methods/mirror.cc index 9d900771b..71faaf591 100644 --- a/methods/mirror.cc +++ b/methods/mirror.cc @@ -57,7 +57,7 @@ using namespace std; */ MirrorMethod::MirrorMethod() - : HttpMethod(), DownloadedMirrorFile(false), Debug(false) + : HttpMethod("mirror"), DownloadedMirrorFile(false), Debug(false) { } @@ -68,7 +68,7 @@ bool MirrorMethod::Configuration(string Message) { if (pkgAcqMethod::Configuration(Message) == false) return false; - Debug = _config->FindB("Debug::Acquire::mirror",false); + Debug = DebugEnabled(); return true; } diff --git a/methods/rred.cc b/methods/rred.cc index 2d2333f91..0c641ad82 100644 --- a/methods/rred.cc +++ b/methods/rred.cc @@ -572,7 +572,7 @@ class RredMethod : public aptMethod { protected: virtual bool URIAcquire(std::string const &Message, FetchItem *Itm) APT_OVERRIDE { - Debug = _config->FindB("Debug::pkgAcquire::RRed", false); + Debug = DebugEnabled(); URI Get = Itm->Uri; std::string Path = Get.Host + Get.Path; // rred:/path - no host diff --git a/methods/rsh.cc b/methods/rsh.cc index 74a908ef7..7b8af6f9b 100644 --- a/methods/rsh.cc +++ b/methods/rsh.cc @@ -383,9 +383,7 @@ bool RSHConn::Get(const char *Path,FileFd &To,unsigned long long Resume, /*}}}*/ // RSHMethod::RSHMethod - Constructor /*{{{*/ -// --------------------------------------------------------------------- -/* */ -RSHMethod::RSHMethod(std::string const &pProg) : aptMethod(pProg.c_str(),"1.0",SendConfig), Prog(pProg) +RSHMethod::RSHMethod(std::string &&pProg) : aptMethod(std::move(pProg),"1.0",SendConfig) { signal(SIGTERM,SigTerm); signal(SIGINT,SigTerm); @@ -399,14 +397,14 @@ bool RSHMethod::Configuration(std::string Message) { // enabling privilege dropping for this method requires configuration… // … which is otherwise lifted straight from root, so use it by default. - _config->Set(std::string("Binary::") + Prog + "::APT::Sandbox::User", ""); + _config->Set(std::string("Binary::") + Binary + "::APT::Sandbox::User", ""); if (aptMethod::Configuration(Message) == false) return false; - std::string const timeconf = std::string("Acquire::") + Prog + "::Timeout"; + std::string const timeconf = std::string("Acquire::") + Binary + "::Timeout"; TimeOut = _config->FindI(timeconf, TimeOut); - std::string const optsconf = std::string("Acquire::") + Prog + "::Options"; + std::string const optsconf = std::string("Acquire::") + Binary + "::Options"; RshOptions = _config->Tree(optsconf.c_str()); return true; @@ -445,7 +443,7 @@ bool RSHMethod::Fetch(FetchItem *Itm) // Connect to the server if (Server == 0 || Server->Comp(Get) == false) { delete Server; - Server = new RSHConn(Prog, Get); + Server = new RSHConn(Binary, Get); } // Could not connect is a transient error.. diff --git a/methods/rsh.h b/methods/rsh.h index 571e38ba6..dee4ad647 100644 --- a/methods/rsh.h +++ b/methods/rsh.h @@ -58,7 +58,6 @@ class RSHConn class RSHMethod : public aptMethod { - std::string const Prog; virtual bool Fetch(FetchItem *Itm) APT_OVERRIDE; virtual bool Configuration(std::string Message) APT_OVERRIDE; @@ -71,7 +70,7 @@ class RSHMethod : public aptMethod public: - explicit RSHMethod(std::string const &Prog); + explicit RSHMethod(std::string &&Prog); }; #endif diff --git a/methods/server.cc b/methods/server.cc index 66551b893..7c85c8abb 100644 --- a/methods/server.cc +++ b/methods/server.cc @@ -313,10 +313,30 @@ ServerMethod::DealWithHeaders(FetchResult &Res) { NextURI = DeQuoteString(Server->Location); URI tmpURI = NextURI; - if (tmpURI.Access == "http" && Binary == "https+http") + if (tmpURI.Access.find('+') != std::string::npos) { - tmpURI.Access = "https+http"; - NextURI = tmpURI; + _error->Error("Server tried to trick us into using a specific implementation: %s", tmpURI.Access.c_str()); + if (Server->HaveContent == true) + return ERROR_WITH_CONTENT_PAGE; + return ERROR_UNRECOVERABLE; + } + URI Uri = Queue->Uri; + if (Binary.find('+') != std::string::npos) + { + auto base = Binary.substr(0, Binary.find('+')); + if (base != tmpURI.Access) + { + tmpURI.Access = base + '+' + tmpURI.Access; + if (tmpURI.Access == Binary) + { + std::string tmpAccess = Uri.Access; + std::swap(tmpURI.Access, Uri.Access); + NextURI = tmpURI; + std::swap(tmpURI.Access, Uri.Access); + } + else + NextURI = tmpURI; + } } if (Queue->Uri == NextURI) { @@ -326,7 +346,7 @@ ServerMethod::DealWithHeaders(FetchResult &Res) return ERROR_WITH_CONTENT_PAGE; return ERROR_UNRECOVERABLE; } - URI Uri = Queue->Uri; + Uri.Access = Binary; // same protocol redirects are okay if (tmpURI.Access == Uri.Access) return TRY_AGAIN_OR_REDIRECT; @@ -334,7 +354,22 @@ ServerMethod::DealWithHeaders(FetchResult &Res) else if ((Uri.Access == "http" || Uri.Access == "https+http") && tmpURI.Access == "https") return TRY_AGAIN_OR_REDIRECT; else - _error->Error("Redirection from %s to '%s' is forbidden", Uri.Access.c_str(), NextURI.c_str()); + { + auto const tmpplus = tmpURI.Access.find('+'); + if (tmpplus != std::string::npos && tmpURI.Access.substr(tmpplus + 1) == "https") + { + auto const uriplus = Uri.Access.find('+'); + if (uriplus == std::string::npos) + { + if (Uri.Access == tmpURI.Access.substr(0, tmpplus)) // foo -> foo+https + return TRY_AGAIN_OR_REDIRECT; + } + else if (Uri.Access.substr(uriplus + 1) == "http" && + Uri.Access.substr(0, uriplus) == tmpURI.Access.substr(0, tmpplus)) // foo+http -> foo+https + return TRY_AGAIN_OR_REDIRECT; + } + } + _error->Error("Redirection from %s to '%s' is forbidden", Uri.Access.c_str(), NextURI.c_str()); } /* else pass through for error message */ } @@ -513,7 +548,7 @@ int ServerMethod::Loop() if (Result != -1 && (Result != 0 || Queue == 0)) { if(FailReason.empty() == false || - _config->FindB("Acquire::http::DependOnSTDIN", true) == true) + ConfigFindB("DependOnSTDIN", true) == true) return 100; else return 0; @@ -524,7 +559,13 @@ int ServerMethod::Loop() // Connect to the server if (Server == 0 || Server->Comp(Queue->Uri) == false) + { Server = CreateServerState(Queue->Uri); + setPostfixForMethodNames(::URI(Queue->Uri).Host.c_str()); + AllowRedirect = ConfigFindB("AllowRedirect", true); + PipelineDepth = ConfigFindI("Pipeline-Depth", 10); + Debug = DebugEnabled(); + } /* If the server has explicitly said this is the last connection then we pre-emptively shut down the pipeline and tear down @@ -747,8 +788,8 @@ unsigned long long ServerMethod::FindMaximumObjectSizeInQueue() const /*{{{*/ return MaxSizeInQueue; } /*}}}*/ -ServerMethod::ServerMethod(char const * const Binary, char const * const Ver,unsigned long const Flags) :/*{{{*/ - aptMethod(Binary, Ver, Flags), Server(nullptr), File(NULL), PipelineDepth(10), +ServerMethod::ServerMethod(std::string &&Binary, char const * const Ver,unsigned long const Flags) :/*{{{*/ + aptMethod(std::move(Binary), Ver, Flags), Server(nullptr), File(NULL), PipelineDepth(10), AllowRedirect(false), Debug(false) { } diff --git a/methods/server.h b/methods/server.h index f2868c96a..f6a635dca 100644 --- a/methods/server.h +++ b/methods/server.h @@ -157,7 +157,7 @@ class ServerMethod : public aptMethod virtual std::unique_ptr<ServerState> CreateServerState(URI const &uri) = 0; virtual void RotateDNS() = 0; - ServerMethod(char const * const Binary, char const * const Ver,unsigned long const Flags); + ServerMethod(std::string &&Binary, char const * const Ver,unsigned long const Flags); virtual ~ServerMethod() {}; }; diff --git a/methods/store.cc b/methods/store.cc index fa02d4597..1faaa4fb4 100644 --- a/methods/store.cc +++ b/methods/store.cc @@ -32,12 +32,15 @@ class StoreMethod : public aptMethod { - std::string const Prog; virtual bool Fetch(FetchItem *Itm) APT_OVERRIDE; public: - explicit StoreMethod(std::string const &pProg) : aptMethod(pProg.c_str(),"1.2",SingleInstance | SendConfig), Prog(pProg) {}; + explicit StoreMethod(std::string &&pProg) : aptMethod(std::move(pProg),"1.2",SingleInstance | SendConfig) + { + if (Binary != "store") + methodNames.insert(methodNames.begin(), "store"); + } }; static bool OpenFileWithCompressorByName(FileFd &fileFd, std::string const &Filename, unsigned int const Mode, std::string const &Name) @@ -70,7 +73,7 @@ bool StoreMethod::Fetch(FetchItem *Itm) /*{{{*/ FileFd From; if (_config->FindB("Method::Compress", false) == false) { - if (OpenFileWithCompressorByName(From, Path, FileFd::ReadOnly, Prog) == false) + if (OpenFileWithCompressorByName(From, Path, FileFd::ReadOnly, Binary) == false) return false; if(From.IsCompressed() && From.FileSize() == 0) return _error->Error(_("Empty files can't be valid archives")); @@ -85,7 +88,7 @@ bool StoreMethod::Fetch(FetchItem *Itm) /*{{{*/ { if (_config->FindB("Method::Compress", false) == false) To.Open(Itm->DestFile, FileFd::WriteOnly | FileFd::Create | FileFd::Atomic, FileFd::Extension); - else if (OpenFileWithCompressorByName(To, Itm->DestFile, FileFd::WriteOnly | FileFd::Create | FileFd::Empty, Prog) == false) + else if (OpenFileWithCompressorByName(To, Itm->DestFile, FileFd::WriteOnly | FileFd::Create | FileFd::Empty, Binary) == false) return false; if (To.IsOpen() == false || To.Failed() == true) diff --git a/test/integration/test-bug-738785-switch-protocol b/test/integration/test-bug-738785-switch-protocol index 8f8216d04..fa6702eb0 100755 --- a/test/integration/test-bug-738785-switch-protocol +++ b/test/integration/test-bug-738785-switch-protocol @@ -63,4 +63,4 @@ ln -s "$OLDMETHODS/https" "$NEWMETHODS" # check that downgrades from https to http are not allowed webserverconfig 'aptwebserver::support::http' 'true' sed -i -e "s#:${APTHTTPPORT}/redirectme#:${APTHTTPSPORT}/downgrademe#" -e 's# http:# https:#' rootdir/etc/apt/sources.list.d/* -testfailure aptget update --allow-insecure-repositories +testfailure aptget update --allow-insecure-repositories -o Acquire::https::Timeout=1 diff --git a/test/integration/test-different-methods-for-same-source b/test/integration/test-different-methods-for-same-source index 6b3f796b1..40138ad5d 100755 --- a/test/integration/test-different-methods-for-same-source +++ b/test/integration/test-different-methods-for-same-source @@ -24,10 +24,23 @@ IFS="$backupIFS" ln -s "${OLDMETHODS}/http" "${NEWMETHODS}/http-ng" changetowebserver +webserverconfig 'aptwebserver::redirect::replace::/redirectme/' "http://localhost:${APTHTTPPORT}/" sed -i -e 's# http:# http-ng:#' $(find rootdir/etc/apt/sources.list.d -name '*-deb-src.list') -testsuccess apt update +testsuccess apt update -o Debug::Acquire::http-ng=1 cp rootdir/tmp/testsuccess.output update.log # all requests are folded into the first Release file testsuccess grep ' http-ng://' update.log testfailure grep ' http://' update.log +# see if method-specific debug was enabled +testsuccess grep '^Answer for: http-ng:' update.log + +rm -rf rootdir/var/lib/apt/lists +sed -i -e "s#:${APTHTTPPORT}/#:${APTHTTPPORT}/redirectme#" rootdir/etc/apt/sources.list.d/* +testsuccess apt update -o Debug::Acquire::http-ng=1 +cp rootdir/tmp/testsuccess.output update.log +# all requests are folded into the first Release file +testsuccess grep ' http-ng://' update.log +testfailure grep '^[^L].* http://' update.log +# see if method-specific debug was enabled +testsuccess grep '^Answer for: http-ng:' update.log diff --git a/test/integration/test-pdiff-usage b/test/integration/test-pdiff-usage index 91528389b..39d847203 100755 --- a/test/integration/test-pdiff-usage +++ b/test/integration/test-pdiff-usage @@ -317,7 +317,7 @@ Debug::Acquire::Transaction "true"; Debug::pkgAcquire::Worker "true"; Debug::Acquire::http "true"; Debug::pkgAcquire "true"; -Debug::pkgAcquire::rred "true";' > rootdir/etc/apt/apt.conf.d/rreddebug.conf +Debug::Acquire::rred "true";' > rootdir/etc/apt/apt.conf.d/rreddebug.conf testcase() { testrun -o Acquire::PDiffs::Merge=0 -o APT::Get::List-Cleanup=1 "$@" |