]> git.saurik.com Git - apt.git/commitdiff
implement generic config fallback for methods
authorDavid Kalnischkies <david@kalnischkies.de>
Sun, 31 Jul 2016 16:05:56 +0000 (18:05 +0200)
committerDavid Kalnischkies <david@kalnischkies.de>
Wed, 10 Aug 2016 21:19:44 +0000 (23:19 +0200)
The https method implemented for a long while now a hardcoded fallback
to the same options in http, which, while it works, is rather inflexible
if we want to allow the methods to use another name to change their
behavior slightly, like apt-transport-tor does to https – most of the
diff being s#https#tor#g which then fails to do the full circle
fallthrough tor -> https -> http for https sources. With this config
infrastructure this could be implemented now.

18 files changed:
methods/aptmethod.h
methods/cdrom.cc
methods/gpgv.cc
methods/http.cc
methods/http.h
methods/http_main.cc
methods/https.cc
methods/https.h
methods/mirror.cc
methods/rred.cc
methods/rsh.cc
methods/rsh.h
methods/server.cc
methods/server.h
methods/store.cc
test/integration/test-bug-738785-switch-protocol
test/integration/test-different-methods-for-same-source
test/integration/test-pdiff-usage

index 0963ea21e9b9003d191d754ae5305d570bb9fafc..38f45170880b67f57e63d989109e4d5d161631a3 100644 (file)
@@ -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>
 
 #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(""));
index 161822ac6e0062cb46b1ca99285c96132a6fe267..87a58e94887a7f008ce6e0dc439321f6f5780bd6 100644 (file)
@@ -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;
index fc6eb91593deab2db2bf3806189f1cc914a581fb..2fed53a39c2147f2ef14b657ef9ae764810fb6c1 100644 (file)
@@ -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;
 }
index b7a3aa4a12fd3d4964b30ca33f40eed995d8b42d..0378d7c608a7037208d91435ad6fd74e29bdb6e1 100644 (file)
@@ -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;
+}
+                                                                       /*}}}*/
index e6b11d642d79171dd1774fb05420c2e2a42ed074..4d22c88f9ca0b0d9c6eab138b634f04c64f2b1ab 100644 (file)
@@ -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
index fa183ddb3ce7119f5b57181886dfcaf28fb4ed48..1e56044b72233dc7789d896847f336d123bfe365 100644 (file)
@@ -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();
 }
index a5a8a7cd1ef183f7a4e00e04725d2f3519f042bf..47dce2ea0d0c57f17e81d44c3c566f14ae59a360 100644 (file)
 #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();
+}
+                                                                       /*}}}*/
index 85cc7824e16802457d5a6b064ff0637e0b4e0d30..04e72e8154e68147472edf325a9dfc00a59a5e27 100644 (file)
@@ -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>
index 9d900771be8255c1b926e604727646307c1aca84..71faaf5916962bb659a0b2a2444d6d3a0699217a 100644 (file)
@@ -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;
 }
index 2d2333f9125179b23c2e01366e7f562e1fe97f28..0c641ad826e65724f18a7aa8c4dd7bc8d1f29cac 100644 (file)
@@ -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
 
index 74a908ef7f2647e3668e8af6203270fc4c3025f8..7b8af6f9b8636cd842313dbcc70e120fb8867e04 100644 (file)
@@ -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..
index 571e38ba64baba53a062987b1f0fb1f9facf0081..dee4ad647fff45afa8adc2e77ab06c753e1e2c35 100644 (file)
@@ -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
index 66551b8931c33eaf3dab77973fe5a88dee9eb928..7c85c8abbf24a183ea78178f36cbe8ebe98af029 100644 (file)
@@ -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)
 {
 }
index f2868c96a533992da004efe996ffb8d05ac3b9e3..f6a635dcac99b32becd3a82ad3ff45b5ebea0587 100644 (file)
@@ -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() {};
 };
 
index fa02d4597cadd22ca00bb0d1611b3111a7b09f6d..1faaa4fb4764c52a3633cec6ab671114480a5613 100644 (file)
 
 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)
index 8f8216d0440418d846f410a8043eccc54b831791..fa6702eb07040a5bf165b20828aab14876ccc243 100755 (executable)
@@ -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
index 6b3f796b179d7078eefc9bf841951b8fe0d1aeab..40138ad5db108d190889c49001ef6a597276c5a6 100755 (executable)
@@ -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
index 91528389b967666a3a386c3b53e136a7af2d5027..39d84720303788bb861446bb9491f741378ade5f 100755 (executable)
@@ -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 "$@"