"Fossies" - the Fresh Open Source Software Archive

Member "apt-2.2.4/methods/connect.cc" (10 Jun 2021, 34754 Bytes) of package /linux/misc/apt-2.2.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "connect.cc" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.2.3_vs_2.2.4.

    1 // -*- mode: cpp; mode: fold -*-
    2 // Description                              /*{{{*/
    3 /* ######################################################################
    4 
    5    Connect - Replacement connect call
    6 
    7    This was originally authored by Jason Gunthorpe <jgg@debian.org>
    8    and is placed in the Public Domain, do with it what you will.
    9       
   10    ##################################################################### */
   11                                     /*}}}*/
   12 // Include Files                            /*{{{*/
   13 #include <config.h>
   14 
   15 #include <apt-pkg/acquire-method.h>
   16 #include <apt-pkg/configuration.h>
   17 #include <apt-pkg/error.h>
   18 #include <apt-pkg/fileutl.h>
   19 #include <apt-pkg/srvrec.h>
   20 #include <apt-pkg/strutl.h>
   21 
   22 #include <gnutls/gnutls.h>
   23 #include <gnutls/x509.h>
   24 
   25 #include <list>
   26 #include <set>
   27 #include <sstream>
   28 #include <string>
   29 #include <errno.h>
   30 #include <stdio.h>
   31 #include <string.h>
   32 #include <unistd.h>
   33 
   34 // Internet stuff
   35 #include <netdb.h>
   36 #include <arpa/inet.h>
   37 #include <netinet/in.h>
   38 #include <sys/select.h>
   39 #include <sys/socket.h>
   40 
   41 #include "aptmethod.h"
   42 #include "connect.h"
   43 #include "rfc2553emu.h"
   44 #include <apti18n.h>
   45                                     /*}}}*/
   46 
   47 static std::string LastHost;
   48 static std::string LastService;
   49 static struct addrinfo *LastHostAddr = 0;
   50 static struct addrinfo *LastUsed = 0;
   51 
   52 static std::vector<SrvRec> SrvRecords;
   53 
   54 // Set of IP/hostnames that we timed out before or couldn't resolve
   55 static std::set<std::string> bad_addr;
   56 
   57 // RotateDNS - Select a new server from a DNS rotation          /*{{{*/
   58 // ---------------------------------------------------------------------
   59 /* This is called during certain errors in order to recover by selecting a 
   60    new server */
   61 void RotateDNS()
   62 {
   63    if (LastUsed != 0 && LastUsed->ai_next != 0)
   64       LastUsed = LastUsed->ai_next;
   65    else
   66       LastUsed = LastHostAddr;
   67 }
   68                                     /*}}}*/
   69 static bool ConnectionAllowed(char const * const Service, std::string const &Host)/*{{{*/
   70 {
   71    if (unlikely(Host.empty())) // the only legal empty host (RFC2782 '.' target) is detected by caller
   72       return false;
   73    if (APT::String::Endswith(Host, ".onion") && _config->FindB("Acquire::BlockDotOnion", true))
   74    {
   75       // TRANSLATOR: %s is e.g. Tor's ".onion" which would likely fail or leak info (RFC7686)
   76       _error->Error(_("Direct connection to %s domains is blocked by default."), ".onion");
   77       if (strcmp(Service, "http") == 0)
   78     _error->Error(_("If you meant to use Tor remember to use %s instead of %s."), "tor+http", "http");
   79       return false;
   80    }
   81    return true;
   82 }
   83                                     /*}}}*/
   84 
   85 // File Descriptor based Fd /*{{{*/
   86 struct FdFd : public MethodFd
   87 {
   88    int fd = -1;
   89    int Fd() APT_OVERRIDE { return fd; }
   90    ssize_t Read(void *buf, size_t count) APT_OVERRIDE { return ::read(fd, buf, count); }
   91    ssize_t Write(void *buf, size_t count) APT_OVERRIDE { return ::write(fd, buf, count); }
   92    int Close() APT_OVERRIDE
   93    {
   94       int result = 0;
   95       if (fd != -1)
   96      result = ::close(fd);
   97       fd = -1;
   98       return result;
   99    }
  100 };
  101 
  102 bool MethodFd::HasPending()
  103 {
  104    return false;
  105 }
  106 std::unique_ptr<MethodFd> MethodFd::FromFd(int iFd)
  107 {
  108    FdFd *fd = new FdFd();
  109    fd->fd = iFd;
  110    return std::unique_ptr<MethodFd>(fd);
  111 }
  112                                     /*}}}*/
  113 // DoConnect - Attempt a connect operation              /*{{{*/
  114 // ---------------------------------------------------------------------
  115 /* This helper function attempts a connection to a single address. */
  116 struct Connection
  117 {
  118    struct addrinfo *Addr;
  119    std::string Host;
  120    aptMethod *Owner;
  121    std::unique_ptr<FdFd> Fd;
  122    char Name[NI_MAXHOST];
  123    char Service[NI_MAXSERV];
  124 
  125    Connection(struct addrinfo *Addr, std::string const &Host, aptMethod *Owner) : Addr(Addr), Host(Host), Owner(Owner), Fd(new FdFd()), Name{0}, Service{0}
  126    {
  127    }
  128 
  129    // Allow moving values, but not connections.
  130    Connection(Connection &&Conn) = default;
  131    Connection(const Connection &Conn) = delete;
  132    Connection &operator=(const Connection &) = delete;
  133    Connection &operator=(Connection &&Conn) = default;
  134 
  135    ~Connection()
  136    {
  137       if (Fd != nullptr)
  138       {
  139      Fd->Close();
  140       }
  141    }
  142 
  143    std::unique_ptr<MethodFd> Take()
  144    {
  145       /* Store the IP we are using.. If something goes
  146       wrong this will get tacked onto the end of the error message */
  147       std::stringstream ss;
  148       ioprintf(ss, _("[IP: %s %s]"), Name, Service);
  149       Owner->SetIP(ss.str());
  150       Owner->Status(_("Connected to %s (%s)"), Host.c_str(), Name);
  151       _error->Discard();
  152       Owner->SetFailReason("");
  153       LastUsed = Addr;
  154       return std::move(Fd);
  155    }
  156 
  157    ResultState DoConnect();
  158 
  159    ResultState CheckError();
  160 };
  161 
  162 ResultState Connection::DoConnect()
  163 {
  164    getnameinfo(Addr->ai_addr,Addr->ai_addrlen,
  165            Name,sizeof(Name),Service,sizeof(Service),
  166            NI_NUMERICHOST|NI_NUMERICSERV);
  167    Owner->Status(_("Connecting to %s (%s)"),Host.c_str(),Name);
  168 
  169    // if that addr did timeout before, we do not try it again
  170    if(bad_addr.find(std::string(Name)) != bad_addr.end())
  171       return ResultState::TRANSIENT_ERROR;
  172       
  173    // Get a socket
  174    if ((static_cast<FdFd *>(Fd.get())->fd = socket(Addr->ai_family, Addr->ai_socktype,
  175                            Addr->ai_protocol)) < 0)
  176    {
  177       _error->Errno("socket", _("Could not create a socket for %s (f=%u t=%u p=%u)"),
  178             Name, Addr->ai_family, Addr->ai_socktype, Addr->ai_protocol);
  179       return ResultState::FATAL_ERROR;
  180    }
  181 
  182    SetNonBlock(Fd->Fd(), true);
  183    if (connect(Fd->Fd(), Addr->ai_addr, Addr->ai_addrlen) < 0 &&
  184        errno != EINPROGRESS)
  185    {
  186       _error->Errno("connect", _("Cannot initiate the connection "
  187                  "to %s:%s (%s)."),
  188             Host.c_str(), Service, Name);
  189       return ResultState::TRANSIENT_ERROR;
  190    }
  191 
  192    return ResultState::SUCCESSFUL;
  193 }
  194 
  195 ResultState Connection::CheckError()
  196 {
  197    // Check the socket for an error condition
  198    unsigned int Err;
  199    unsigned int Len = sizeof(Err);
  200    if (getsockopt(Fd->Fd(), SOL_SOCKET, SO_ERROR, &Err, &Len) != 0)
  201    {
  202       _error->Errno("getsockopt", _("Failed"));
  203       return ResultState::FATAL_ERROR;
  204    }
  205 
  206    if (Err != 0)
  207    {
  208       errno = Err;
  209       if(errno == ECONNREFUSED)
  210          Owner->SetFailReason("ConnectionRefused");
  211       else if (errno == ETIMEDOUT)
  212      Owner->SetFailReason("ConnectionTimedOut");
  213       bad_addr.insert(bad_addr.begin(), std::string(Name));
  214       _error->Errno("connect", _("Could not connect to %s:%s (%s)."), Host.c_str(),
  215             Service, Name);
  216       return ResultState::TRANSIENT_ERROR;
  217    }
  218 
  219    Owner->SetFailReason("");
  220 
  221    return ResultState::SUCCESSFUL;
  222 }
  223                                     /*}}}*/
  224 // Order the given host names returned by getaddrinfo()         /*{{{*/
  225 static std::vector<struct addrinfo *> OrderAddresses(struct addrinfo *CurHost)
  226 {
  227    std::vector<struct addrinfo *> preferredAddrs;
  228    std::vector<struct addrinfo *> otherAddrs;
  229    std::vector<struct addrinfo *> allAddrs;
  230 
  231    // Partition addresses into preferred and other address families
  232    while (CurHost != 0)
  233    {
  234       if (preferredAddrs.empty() || CurHost->ai_family == preferredAddrs[0]->ai_family)
  235      preferredAddrs.push_back(CurHost);
  236       else
  237      otherAddrs.push_back(CurHost);
  238 
  239       // Ignore UNIX domain sockets
  240       do
  241       {
  242      CurHost = CurHost->ai_next;
  243       } while (CurHost != 0 && CurHost->ai_family == AF_UNIX);
  244 
  245       /* If we reached the end of the search list then wrap around to the
  246      start */
  247       if (CurHost == 0 && LastUsed != 0)
  248      CurHost = LastHostAddr;
  249 
  250       // Reached the end of the search cycle
  251       if (CurHost == LastUsed)
  252      break;
  253    }
  254 
  255    // Build a new address vector alternating between preferred and other
  256    for (auto prefIter = preferredAddrs.cbegin(), otherIter = otherAddrs.cbegin();
  257     prefIter != preferredAddrs.end() || otherIter != otherAddrs.end();)
  258    {
  259       if (prefIter != preferredAddrs.end())
  260      allAddrs.push_back(*prefIter++);
  261       if (otherIter != otherAddrs.end())
  262      allAddrs.push_back(*otherIter++);
  263    }
  264 
  265    return allAddrs;
  266 }
  267                                     /*}}}*/
  268 // Check for errors and report them                 /*{{{*/
  269 static ResultState WaitAndCheckErrors(std::list<Connection> &Conns, std::unique_ptr<MethodFd> &Fd, long TimeoutMsec, bool ReportTimeout)
  270 {
  271    // The last error detected
  272    ResultState Result = ResultState::TRANSIENT_ERROR;
  273 
  274    struct timeval tv = {
  275       // Split our millisecond timeout into seconds and microseconds
  276       .tv_sec = TimeoutMsec / 1000,
  277       .tv_usec = (TimeoutMsec % 1000) * 1000,
  278    };
  279 
  280    // We will return once we have no more connections, a time out, or
  281    // a success.
  282    while (!Conns.empty())
  283    {
  284       fd_set Set;
  285       int nfds = -1;
  286 
  287       FD_ZERO(&Set);
  288 
  289       for (auto &Conn : Conns)
  290       {
  291      int fd = Conn.Fd->Fd();
  292      FD_SET(fd, &Set);
  293      nfds = std::max(nfds, fd);
  294       }
  295 
  296       {
  297      int Res;
  298      do
  299      {
  300         Res = select(nfds + 1, 0, &Set, 0, (TimeoutMsec != 0 ? &tv : 0));
  301      } while (Res < 0 && errno == EINTR);
  302 
  303      if (Res == 0)
  304      {
  305         if (ReportTimeout)
  306         {
  307            for (auto &Conn : Conns)
  308            {
  309           Conn.Owner->SetFailReason("Timeout");
  310           bad_addr.insert(bad_addr.begin(), Conn.Name);
  311           _error->Error(_("Could not connect to %s:%s (%s), "
  312                   "connection timed out"),
  313                 Conn.Host.c_str(), Conn.Service, Conn.Name);
  314            }
  315         }
  316         return ResultState::TRANSIENT_ERROR;
  317      }
  318       }
  319 
  320       // iterate over connections, remove failed ones, and return if
  321       // there was a successful one.
  322       for (auto ConnI = Conns.begin(); ConnI != Conns.end();)
  323       {
  324      if (!FD_ISSET(ConnI->Fd->Fd(), &Set))
  325      {
  326         ConnI++;
  327         continue;
  328      }
  329 
  330      Result = ConnI->CheckError();
  331      if (Result == ResultState::SUCCESSFUL)
  332      {
  333         Fd = ConnI->Take();
  334         return Result;
  335      }
  336 
  337      // Connection failed. Erase it and continue to next position
  338      ConnI = Conns.erase(ConnI);
  339       }
  340    }
  341 
  342    return Result;
  343 }
  344                                     /*}}}*/
  345 // Connect to a given Hostname                      /*{{{*/
  346 static ResultState ConnectToHostname(std::string const &Host, int const Port,
  347                      const char *const Service, int DefPort, std::unique_ptr<MethodFd> &Fd,
  348                      unsigned long const TimeOut, aptMethod *const Owner)
  349 {
  350    if (ConnectionAllowed(Service, Host) == false)
  351       return ResultState::FATAL_ERROR;
  352 
  353    // Used by getaddrinfo(); prefer port if given, else fallback to service
  354    std::string ServiceNameOrPort = Port != 0 ? std::to_string(Port) : Service;
  355    
  356    /* We used a cached address record.. Yes this is against the spec but
  357       the way we have setup our rotating dns suggests that this is more
  358       sensible */
  359    if (LastHost != Host || LastService != ServiceNameOrPort)
  360    {
  361       Owner->Status(_("Connecting to %s"),Host.c_str());
  362 
  363       // Free the old address structure
  364       if (LastHostAddr != 0)
  365       {
  366      freeaddrinfo(LastHostAddr);
  367      LastHostAddr = 0;
  368      LastUsed = 0;
  369       }
  370       
  371       // We only understand SOCK_STREAM sockets.
  372       struct addrinfo Hints;
  373       memset(&Hints,0,sizeof(Hints));
  374       Hints.ai_socktype = SOCK_STREAM;
  375       Hints.ai_flags = 0;
  376 #ifdef AI_IDN
  377       if (_config->FindB("Acquire::Connect::IDN", true) == true)
  378      Hints.ai_flags |= AI_IDN;
  379 #endif
  380       // see getaddrinfo(3): only return address if system has such a address configured
  381       // useful if system is ipv4 only, to not get ipv6, but that fails if the system has
  382       // no address configured: e.g. offline and trying to connect to localhost.
  383       if (_config->FindB("Acquire::Connect::AddrConfig", true) == true)
  384      Hints.ai_flags |= AI_ADDRCONFIG;
  385       Hints.ai_protocol = 0;
  386       
  387       if(_config->FindB("Acquire::ForceIPv4", false) == true)
  388          Hints.ai_family = AF_INET;
  389       else if(_config->FindB("Acquire::ForceIPv6", false) == true)
  390          Hints.ai_family = AF_INET6;
  391       else
  392          Hints.ai_family = AF_UNSPEC;
  393 
  394       // if we couldn't resolve the host before, we don't try now
  395       if (bad_addr.find(Host) != bad_addr.end())
  396       {
  397      _error->Error(_("Could not resolve '%s'"), Host.c_str());
  398      return ResultState::TRANSIENT_ERROR;
  399       }
  400 
  401       // Resolve both the host and service simultaneously
  402       while (1)
  403       {
  404      int Res;
  405      if ((Res = getaddrinfo(Host.c_str(), ServiceNameOrPort.c_str(), &Hints, &LastHostAddr)) != 0 ||
  406          LastHostAddr == 0)
  407      {
  408         if (Res == EAI_NONAME || Res == EAI_SERVICE)
  409         {
  410            if (DefPort != 0)
  411            {
  412           ServiceNameOrPort = std::to_string(DefPort);
  413           DefPort = 0;
  414           continue;
  415            }
  416            bad_addr.insert(bad_addr.begin(), Host);
  417            Owner->SetFailReason("ResolveFailure");
  418            _error->Error(_("Could not resolve '%s'"), Host.c_str());
  419            return ResultState::TRANSIENT_ERROR;
  420         }
  421         
  422         if (Res == EAI_AGAIN)
  423         {
  424            Owner->SetFailReason("TmpResolveFailure");
  425            _error->Error(_("Temporary failure resolving '%s'"),
  426                  Host.c_str());
  427            return ResultState::TRANSIENT_ERROR;
  428         }
  429         if (Res == EAI_SYSTEM)
  430            _error->Errno("getaddrinfo", _("System error resolving '%s:%s'"),
  431                  Host.c_str(), ServiceNameOrPort.c_str());
  432         else
  433            _error->Error(_("Something wicked happened resolving '%s:%s' (%i - %s)"),
  434                  Host.c_str(), ServiceNameOrPort.c_str(), Res, gai_strerror(Res));
  435         return ResultState::TRANSIENT_ERROR;
  436      }
  437      break;
  438       }
  439       
  440       LastHost = Host;
  441       LastService = ServiceNameOrPort;
  442    }
  443 
  444    // When we have an IP rotation stay with the last IP.
  445    auto Addresses = OrderAddresses(LastUsed != nullptr ? LastUsed : LastHostAddr);
  446    std::list<Connection> Conns;
  447    ResultState Result = ResultState::SUCCESSFUL;
  448 
  449    for (auto Addr : Addresses)
  450    {
  451       Connection Conn(Addr, Host, Owner);
  452       if (Conn.DoConnect() != ResultState::SUCCESSFUL)
  453      continue;
  454 
  455       Conns.push_back(std::move(Conn));
  456 
  457       Result = WaitAndCheckErrors(Conns, Fd, Owner->ConfigFindI("ConnectionAttemptDelayMsec", 250), false);
  458 
  459       if (Result == ResultState::SUCCESSFUL)
  460      return ResultState::SUCCESSFUL;
  461    }
  462 
  463    if (!Conns.empty())
  464       return WaitAndCheckErrors(Conns, Fd, TimeOut * 1000, true);
  465    if (Result != ResultState::SUCCESSFUL)
  466       return Result;
  467    if (_error->PendingError() == true)
  468       return ResultState::FATAL_ERROR;
  469    _error->Error(_("Unable to connect to %s:%s:"), Host.c_str(), ServiceNameOrPort.c_str());
  470    return ResultState::TRANSIENT_ERROR;
  471 }
  472                                     /*}}}*/
  473 // Connect - Connect to a server                    /*{{{*/
  474 // ---------------------------------------------------------------------
  475 /* Performs a connection to the server (including SRV record lookup) */
  476 ResultState Connect(std::string Host, int Port, const char *Service,
  477             int DefPort, std::unique_ptr<MethodFd> &Fd,
  478             unsigned long TimeOut, aptMethod *Owner)
  479 {
  480    if (_error->PendingError() == true)
  481       return ResultState::FATAL_ERROR;
  482 
  483    if (ConnectionAllowed(Service, Host) == false)
  484       return ResultState::FATAL_ERROR;
  485 
  486    // Used by getaddrinfo(); prefer port if given, else fallback to service
  487    std::string ServiceNameOrPort = Port != 0 ? std::to_string(Port) : Service;
  488 
  489    if(LastHost != Host || LastService != ServiceNameOrPort)
  490    {
  491       SrvRecords.clear();
  492       if (_config->FindB("Acquire::EnableSrvRecords", true) == true)
  493       {
  494          GetSrvRecords(Host, DefPort, SrvRecords);
  495      // RFC2782 defines that a lonely '.' target is an abort reason
  496      if (SrvRecords.size() == 1 && SrvRecords[0].target.empty())
  497      {
  498         _error->Error("SRV records for %s indicate that "
  499               "%s service is not available at this domain",
  500               Host.c_str(), Service);
  501         return ResultState::FATAL_ERROR;
  502      }
  503       }
  504    }
  505 
  506    size_t stackSize = 0;
  507    // try to connect in the priority order of the srv records
  508    std::string initialHost{std::move(Host)};
  509    auto const initialPort = Port;
  510    while(SrvRecords.empty() == false)
  511    {
  512       _error->PushToStack();
  513       ++stackSize;
  514       // PopFromSrvRecs will also remove the server
  515       auto Srv = PopFromSrvRecs(SrvRecords);
  516       Host = Srv.target;
  517       Port = Srv.port;
  518       auto const ret = ConnectToHostname(Host, Port, Service, DefPort, Fd, TimeOut, Owner);
  519       if (ret == ResultState::SUCCESSFUL)
  520       {
  521      while(stackSize--)
  522         _error->RevertToStack();
  523      return ret;
  524       }
  525    }
  526    Host = std::move(initialHost);
  527    Port = initialPort;
  528 
  529    // we have no (good) SrvRecords for this host, connect right away
  530    _error->PushToStack();
  531    ++stackSize;
  532    auto const ret = ConnectToHostname(Host, Port, Service, DefPort, Fd,
  533      TimeOut, Owner);
  534    while(stackSize--)
  535       if (ret == ResultState::SUCCESSFUL)
  536      _error->RevertToStack();
  537       else
  538      _error->MergeWithStack();
  539    return ret;
  540 }
  541                                     /*}}}*/
  542 // UnwrapSocks - Handle SOCKS setup                 /*{{{*/
  543 // ---------------------------------------------------------------------
  544 /* This does socks magic */
  545 static bool TalkToSocksProxy(int const ServerFd, std::string const &Proxy,
  546                  char const *const type, bool const ReadWrite, uint8_t *const ToFrom,
  547                  unsigned int const Size, unsigned int const Timeout)
  548 {
  549    if (WaitFd(ServerFd, ReadWrite, Timeout) == false)
  550    {
  551       if (ReadWrite)
  552      return _error->Error("Timed out while waiting to write '%s' to proxy %s", type, URI::SiteOnly(Proxy).c_str());
  553       else
  554      return _error->Error("Timed out while waiting to read '%s' from proxy %s", type, URI::SiteOnly(Proxy).c_str());
  555    }
  556    if (ReadWrite == false)
  557    {
  558       if (FileFd::Read(ServerFd, ToFrom, Size) == false)
  559      return _error->Error("Reading the %s from SOCKS proxy %s failed", type, URI::SiteOnly(Proxy).c_str());
  560    }
  561    else
  562    {
  563       if (FileFd::Write(ServerFd, ToFrom, Size) == false)
  564      return _error->Error("Writing the %s to SOCKS proxy %s failed", type, URI::SiteOnly(Proxy).c_str());
  565    }
  566    return true;
  567 }
  568 
  569 ResultState UnwrapSocks(std::string Host, int Port, URI Proxy, std::unique_ptr<MethodFd> &Fd,
  570             unsigned long Timeout, aptMethod *Owner)
  571 {
  572    /* We implement a very basic SOCKS5 client here complying mostly to RFC1928 expect
  573     * for not offering GSSAPI auth which is a must (we only do no or user/pass auth).
  574     * We also expect the SOCKS5 server to do hostname lookup (aka socks5h) */
  575    std::string const ProxyInfo = URI::SiteOnly(Proxy);
  576    Owner->Status(_("Connecting to %s (%s)"), "SOCKS5h proxy", ProxyInfo.c_str());
  577 #define APT_WriteOrFail(TYPE, DATA, LENGTH)                                               \
  578    if (TalkToSocksProxy(Fd->Fd(), ProxyInfo, TYPE, true, DATA, LENGTH, Timeout) == false) \
  579    return ResultState::TRANSIENT_ERROR
  580 #define APT_ReadOrFail(TYPE, DATA, LENGTH)                                                 \
  581    if (TalkToSocksProxy(Fd->Fd(), ProxyInfo, TYPE, false, DATA, LENGTH, Timeout) == false) \
  582    return ResultState::TRANSIENT_ERROR
  583    if (Host.length() > 255)
  584    {
  585       _error->Error("Can't use SOCKS5h as hostname %s is too long!", Host.c_str());
  586       return ResultState::FATAL_ERROR;
  587    }
  588    if (Proxy.User.length() > 255 || Proxy.Password.length() > 255)
  589    {
  590       _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());
  591       return ResultState::FATAL_ERROR;
  592    }
  593    if (Proxy.User.empty())
  594    {
  595       uint8_t greeting[] = {0x05, 0x01, 0x00};
  596       APT_WriteOrFail("greet-1", greeting, sizeof(greeting));
  597    }
  598    else
  599    {
  600       uint8_t greeting[] = {0x05, 0x02, 0x00, 0x02};
  601       APT_WriteOrFail("greet-2", greeting, sizeof(greeting));
  602    }
  603    uint8_t greeting[2];
  604    APT_ReadOrFail("greet back", greeting, sizeof(greeting));
  605    if (greeting[0] != 0x05)
  606    {
  607       _error->Error("SOCKS proxy %s greets back with wrong version: %d", ProxyInfo.c_str(), greeting[0]);
  608       return ResultState::FATAL_ERROR;
  609    }
  610    if (greeting[1] == 0x00)
  611       ; // no auth has no method-dependent sub-negotiations
  612    else if (greeting[1] == 0x02)
  613    {
  614       if (Proxy.User.empty())
  615       {
  616      _error->Error("SOCKS proxy %s negotiated user&pass auth, but we had not offered it!", ProxyInfo.c_str());
  617      return ResultState::FATAL_ERROR;
  618       }
  619       // user&pass auth sub-negotiations are defined by RFC1929
  620       std::vector<uint8_t> auth = {{0x01, static_cast<uint8_t>(Proxy.User.length())}};
  621       std::copy(Proxy.User.begin(), Proxy.User.end(), std::back_inserter(auth));
  622       auth.push_back(static_cast<uint8_t>(Proxy.Password.length()));
  623       std::copy(Proxy.Password.begin(), Proxy.Password.end(), std::back_inserter(auth));
  624       APT_WriteOrFail("user&pass auth", auth.data(), auth.size());
  625       uint8_t authstatus[2];
  626       APT_ReadOrFail("auth report", authstatus, sizeof(authstatus));
  627       if (authstatus[0] != 0x01)
  628       {
  629      _error->Error("SOCKS proxy %s auth status response with wrong version: %d", ProxyInfo.c_str(), authstatus[0]);
  630      return ResultState::FATAL_ERROR;
  631       }
  632       if (authstatus[1] != 0x00)
  633       {
  634      _error->Error("SOCKS proxy %s reported authorization failure: username or password incorrect? (%d)", ProxyInfo.c_str(), authstatus[1]);
  635      return ResultState::FATAL_ERROR;
  636       }
  637    }
  638    else
  639    {
  640       _error->Error("SOCKS proxy %s greets back having not found a common authorization method: %d", ProxyInfo.c_str(), greeting[1]);
  641       return ResultState::FATAL_ERROR;
  642    }
  643    union {
  644       uint16_t *i;
  645       uint8_t *b;
  646    } portu;
  647    uint16_t port = htons(static_cast<uint16_t>(Port));
  648    portu.i = &port;
  649    std::vector<uint8_t> request = {{0x05, 0x01, 0x00, 0x03, static_cast<uint8_t>(Host.length())}};
  650    std::copy(Host.begin(), Host.end(), std::back_inserter(request));
  651    request.push_back(portu.b[0]);
  652    request.push_back(portu.b[1]);
  653    APT_WriteOrFail("request", request.data(), request.size());
  654    uint8_t response[4];
  655    APT_ReadOrFail("first part of response", response, sizeof(response));
  656    if (response[0] != 0x05)
  657    {
  658       _error->Error("SOCKS proxy %s response with wrong version: %d", ProxyInfo.c_str(), response[0]);
  659       return ResultState::FATAL_ERROR;
  660    }
  661    if (response[2] != 0x00)
  662    {
  663       _error->Error("SOCKS proxy %s has unexpected non-zero reserved field value: %d", ProxyInfo.c_str(), response[2]);
  664       return ResultState::FATAL_ERROR;
  665    }
  666    std::string bindaddr;
  667    if (response[3] == 0x01) // IPv4 address
  668    {
  669       uint8_t ip4port[6];
  670       APT_ReadOrFail("IPv4+Port of response", ip4port, sizeof(ip4port));
  671       portu.b[0] = ip4port[4];
  672       portu.b[1] = ip4port[5];
  673       port = ntohs(*portu.i);
  674       strprintf(bindaddr, "%d.%d.%d.%d:%d", ip4port[0], ip4port[1], ip4port[2], ip4port[3], port);
  675    }
  676    else if (response[3] == 0x03) // hostname
  677    {
  678       uint8_t namelength;
  679       APT_ReadOrFail("hostname length of response", &namelength, 1);
  680       uint8_t hostname[namelength + 2];
  681       APT_ReadOrFail("hostname of response", hostname, sizeof(hostname));
  682       portu.b[0] = hostname[namelength];
  683       portu.b[1] = hostname[namelength + 1];
  684       port = ntohs(*portu.i);
  685       hostname[namelength] = '\0';
  686       strprintf(bindaddr, "%s:%d", hostname, port);
  687    }
  688    else if (response[3] == 0x04) // IPv6 address
  689    {
  690       uint8_t ip6port[18];
  691       APT_ReadOrFail("IPv6+port of response", ip6port, sizeof(ip6port));
  692       portu.b[0] = ip6port[16];
  693       portu.b[1] = ip6port[17];
  694       port = ntohs(*portu.i);
  695       strprintf(bindaddr, "[%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X]:%d",
  696         ip6port[0], ip6port[1], ip6port[2], ip6port[3], ip6port[4], ip6port[5], ip6port[6], ip6port[7],
  697         ip6port[8], ip6port[9], ip6port[10], ip6port[11], ip6port[12], ip6port[13], ip6port[14], ip6port[15],
  698         port);
  699    }
  700    else
  701    {
  702       _error->Error("SOCKS proxy %s destination address is of unknown type: %d",
  703             ProxyInfo.c_str(), response[3]);
  704       return ResultState::FATAL_ERROR;
  705    }
  706    if (response[1] != 0x00)
  707    {
  708       char const *errstr = nullptr;
  709       auto errcode = response[1];
  710       bool Transient = false;
  711       // Tor error reporting can be a bit arcane, lets try to detect & fix it up
  712       if (bindaddr == "0.0.0.0:0")
  713       {
  714      auto const lastdot = Host.rfind('.');
  715      if (lastdot == std::string::npos || Host.substr(lastdot) != ".onion")
  716         ;
  717      else if (errcode == 0x01)
  718      {
  719         auto const prevdot = Host.rfind('.', lastdot - 1);
  720         if (prevdot == std::string::npos && (lastdot == 16 || lastdot == 56))
  721            ; // valid .onion address
  722         else if (prevdot != std::string::npos && ((lastdot - prevdot) == 17 || (lastdot - prevdot) == 57))
  723            ; // valid .onion address with subdomain(s)
  724         else
  725         {
  726            errstr = "Invalid hostname: onion service name must be either 16 or 56 characters long";
  727            Owner->SetFailReason("SOCKS");
  728         }
  729      }
  730      // in all likelihood the service is either down or the address has
  731      // a typo and so "Host unreachable" is the better understood error
  732      // compared to the technically correct "TLL expired".
  733      else if (errcode == 0x06)
  734         errcode = 0x04;
  735       }
  736       if (errstr == nullptr)
  737       {
  738      switch (errcode)
  739      {
  740      case 0x01:
  741         errstr = "general SOCKS server failure";
  742         Owner->SetFailReason("SOCKS");
  743         break;
  744      case 0x02:
  745         errstr = "connection not allowed by ruleset";
  746         Owner->SetFailReason("SOCKS");
  747         break;
  748      case 0x03:
  749         errstr = "Network unreachable";
  750         Owner->SetFailReason("ConnectionTimedOut");
  751         Transient = true;
  752         break;
  753      case 0x04:
  754         errstr = "Host unreachable";
  755         Owner->SetFailReason("ConnectionTimedOut");
  756         Transient = true;
  757         break;
  758      case 0x05:
  759         errstr = "Connection refused";
  760         Owner->SetFailReason("ConnectionRefused");
  761         Transient = true;
  762         break;
  763      case 0x06:
  764         errstr = "TTL expired";
  765         Owner->SetFailReason("Timeout");
  766         Transient = true;
  767         break;
  768      case 0x07:
  769         errstr = "Command not supported";
  770         Owner->SetFailReason("SOCKS");
  771         break;
  772      case 0x08:
  773         errstr = "Address type not supported";
  774         Owner->SetFailReason("SOCKS");
  775         break;
  776      default:
  777         errstr = "Unknown error";
  778         Owner->SetFailReason("SOCKS");
  779         break;
  780      }
  781       }
  782       _error->Error("SOCKS proxy %s could not connect to %s (%s) due to: %s (%d)",
  783             ProxyInfo.c_str(), Host.c_str(), bindaddr.c_str(), errstr, response[1]);
  784       return Transient ? ResultState::TRANSIENT_ERROR : ResultState::FATAL_ERROR;
  785    }
  786    else if (Owner->DebugEnabled())
  787       ioprintf(std::clog, "http: SOCKS proxy %s connection established to %s (%s)\n",
  788            ProxyInfo.c_str(), Host.c_str(), bindaddr.c_str());
  789 
  790    if (WaitFd(Fd->Fd(), true, Timeout) == false)
  791    {
  792       _error->Error("SOCKS proxy %s reported connection to %s (%s), but timed out",
  793             ProxyInfo.c_str(), Host.c_str(), bindaddr.c_str());
  794       return ResultState::TRANSIENT_ERROR;
  795    }
  796 #undef APT_ReadOrFail
  797 #undef APT_WriteOrFail
  798 
  799    return ResultState::SUCCESSFUL;
  800 }
  801                                     /*}}}*/
  802 // UnwrapTLS - Handle TLS connections                   /*{{{*/
  803 // ---------------------------------------------------------------------
  804 /* Performs a TLS handshake on the socket */
  805 struct TlsFd : public MethodFd
  806 {
  807    std::unique_ptr<MethodFd> UnderlyingFd;
  808    gnutls_session_t session;
  809    gnutls_certificate_credentials_t credentials;
  810    std::string hostname;
  811    unsigned long Timeout;
  812 
  813    int Fd() APT_OVERRIDE { return UnderlyingFd->Fd(); }
  814 
  815    ssize_t Read(void *buf, size_t count) APT_OVERRIDE
  816    {
  817       return HandleError(gnutls_record_recv(session, buf, count));
  818    }
  819    ssize_t Write(void *buf, size_t count) APT_OVERRIDE
  820    {
  821       return HandleError(gnutls_record_send(session, buf, count));
  822    }
  823 
  824    ssize_t DoTLSHandshake()
  825    {
  826       int err;
  827       // Do the handshake. Our socket is non-blocking, so we need to call WaitFd()
  828       // accordingly.
  829       do
  830       {
  831          err = gnutls_handshake(session);
  832          if ((err == GNUTLS_E_INTERRUPTED || err == GNUTLS_E_AGAIN) &&
  833              WaitFd(this->Fd(), gnutls_record_get_direction(session) == 1, Timeout) == false)
  834          {
  835             _error->Errno("select", "Could not wait for server fd");
  836             return err;
  837          }
  838       } while (err < 0 && gnutls_error_is_fatal(err) == 0);
  839 
  840       if (err < 0)
  841       {
  842          // Print reason why validation failed.
  843          if (err == GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR)
  844          {
  845             gnutls_datum_t txt;
  846             auto type = gnutls_certificate_type_get(session);
  847             auto status = gnutls_session_get_verify_cert_status(session);
  848             if (gnutls_certificate_verification_status_print(status, type, &txt, 0) == 0)
  849             {
  850                _error->Error("Certificate verification failed: %s", txt.data);
  851             }
  852             gnutls_free(txt.data);
  853          }
  854          _error->Error("Could not handshake: %s", gnutls_strerror(err));
  855       }
  856       return err;
  857    }
  858 
  859    template <typename T>
  860    T HandleError(T err)
  861    {
  862       // Server may request re-handshake if client certificates need to be provided
  863       // based on resource requested
  864       if (err == GNUTLS_E_REHANDSHAKE)
  865       {
  866         int rc = DoTLSHandshake();
  867     // Only reset err if DoTLSHandshake() fails.
  868         // Otherwise, we want to follow the original error path and set errno to EAGAIN
  869         // so that the request is retried.
  870         if (rc < 0)
  871           err = rc;
  872       }
  873 
  874       if (err < 0 && gnutls_error_is_fatal(err))
  875      errno = EIO;
  876       else if (err < 0)
  877      errno = EAGAIN;
  878       else
  879      errno = 0;
  880       return err;
  881    }
  882 
  883    int Close() APT_OVERRIDE
  884    {
  885       auto err = HandleError(gnutls_bye(session, GNUTLS_SHUT_RDWR));
  886       auto lower = UnderlyingFd->Close();
  887       return err < 0 ? HandleError(err) : lower;
  888    }
  889 
  890    bool HasPending() APT_OVERRIDE
  891    {
  892       return gnutls_record_check_pending(session) > 0;
  893    }
  894 };
  895 
  896 ResultState UnwrapTLS(std::string const &Host, std::unique_ptr<MethodFd> &Fd,
  897               unsigned long Timeout, aptMethod *Owner)
  898 {
  899    if (_config->FindB("Acquire::AllowTLS", true) == false)
  900    {
  901       _error->Error("TLS support has been disabled: Acquire::AllowTLS is false.");
  902       return ResultState::FATAL_ERROR;
  903    }
  904 
  905    int err;
  906    TlsFd *tlsFd = new TlsFd();
  907 
  908    tlsFd->hostname = Host;
  909    tlsFd->UnderlyingFd = MethodFd::FromFd(-1); // For now
  910    tlsFd->Timeout = Timeout;
  911 
  912    if ((err = gnutls_init(&tlsFd->session, GNUTLS_CLIENT | GNUTLS_NONBLOCK)) < 0)
  913    {
  914       _error->Error("Internal error: could not allocate credentials: %s", gnutls_strerror(err));
  915       return ResultState::FATAL_ERROR;
  916    }
  917 
  918    FdFd *fdfd = dynamic_cast<FdFd *>(Fd.get());
  919    if (fdfd != nullptr)
  920    {
  921       gnutls_transport_set_int(tlsFd->session, fdfd->fd);
  922    }
  923    else
  924    {
  925       gnutls_transport_set_ptr(tlsFd->session, Fd.get());
  926       gnutls_transport_set_pull_function(tlsFd->session,
  927                      [](gnutls_transport_ptr_t p, void *buf, size_t size) -> ssize_t {
  928                         return reinterpret_cast<MethodFd *>(p)->Read(buf, size);
  929                      });
  930       gnutls_transport_set_push_function(tlsFd->session,
  931                      [](gnutls_transport_ptr_t p, const void *buf, size_t size) -> ssize_t {
  932                         return reinterpret_cast<MethodFd *>(p)->Write((void *)buf, size);
  933                      });
  934    }
  935 
  936    if ((err = gnutls_certificate_allocate_credentials(&tlsFd->credentials)) < 0)
  937    {
  938       _error->Error("Internal error: could not allocate credentials: %s", gnutls_strerror(err));
  939       return ResultState::FATAL_ERROR;
  940    }
  941 
  942    // Credential setup
  943    std::string fileinfo = Owner->ConfigFind("CaInfo", "");
  944    if (fileinfo.empty())
  945    {
  946       // No CaInfo specified, use system trust store.
  947       err = gnutls_certificate_set_x509_system_trust(tlsFd->credentials);
  948       if (err == 0)
  949      Owner->Warning("No system certificates available. Try installing ca-certificates.");
  950       else if (err < 0)
  951       {
  952      _error->Error("Could not load system TLS certificates: %s", gnutls_strerror(err));
  953      return ResultState::FATAL_ERROR;
  954       }
  955    }
  956    else
  957    {
  958       // CA location has been set, use the specified one instead
  959       gnutls_certificate_set_verify_flags(tlsFd->credentials, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
  960       err = gnutls_certificate_set_x509_trust_file(tlsFd->credentials, fileinfo.c_str(), GNUTLS_X509_FMT_PEM);
  961       if (err < 0)
  962       {
  963      _error->Error("Could not load certificates from %s (CaInfo option): %s", fileinfo.c_str(), gnutls_strerror(err));
  964      return ResultState::FATAL_ERROR;
  965       }
  966    }
  967 
  968    if (!Owner->ConfigFind("IssuerCert", "").empty())
  969    {
  970       _error->Error("The option '%s' is not supported anymore", "IssuerCert");
  971       return ResultState::FATAL_ERROR;
  972    }
  973    if (!Owner->ConfigFind("SslForceVersion", "").empty())
  974    {
  975       _error->Error("The option '%s' is not supported anymore", "SslForceVersion");
  976       return ResultState::FATAL_ERROR;
  977    }
  978 
  979    // For client authentication, certificate file ...
  980    std::string const cert = Owner->ConfigFind("SslCert", "");
  981    std::string const key = Owner->ConfigFind("SslKey", "");
  982    if (cert.empty() == false)
  983    {
  984       if ((err = gnutls_certificate_set_x509_key_file(
  985            tlsFd->credentials,
  986            cert.c_str(),
  987            key.empty() ? cert.c_str() : key.c_str(),
  988            GNUTLS_X509_FMT_PEM)) < 0)
  989       {
  990      _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));
  991      return ResultState::FATAL_ERROR;
  992       }
  993    }
  994 
  995    // CRL file
  996    std::string const crlfile = Owner->ConfigFind("CrlFile", "");
  997    if (crlfile.empty() == false)
  998    {
  999       if ((err = gnutls_certificate_set_x509_crl_file(tlsFd->credentials,
 1000                               crlfile.c_str(),
 1001                               GNUTLS_X509_FMT_PEM)) < 0)
 1002       {
 1003      _error->Error("Could not load custom certificate revocation list %s (CrlFile option): %s", crlfile.c_str(), gnutls_strerror(err));
 1004      return ResultState::FATAL_ERROR;
 1005       }
 1006    }
 1007 
 1008    if ((err = gnutls_credentials_set(tlsFd->session, GNUTLS_CRD_CERTIFICATE, tlsFd->credentials)) < 0)
 1009    {
 1010       _error->Error("Internal error: Could not add certificates to session: %s", gnutls_strerror(err));
 1011       return ResultState::FATAL_ERROR;
 1012    }
 1013 
 1014    if ((err = gnutls_set_default_priority(tlsFd->session)) < 0)
 1015    {
 1016       _error->Error("Internal error: Could not set algorithm preferences: %s", gnutls_strerror(err));
 1017       return ResultState::FATAL_ERROR;
 1018    }
 1019 
 1020    if (Owner->ConfigFindB("Verify-Peer", true))
 1021    {
 1022       gnutls_session_set_verify_cert(tlsFd->session, Owner->ConfigFindB("Verify-Host", true) ? tlsFd->hostname.c_str() : nullptr, 0);
 1023    }
 1024 
 1025    // set SNI only if the hostname is really a name and not an address
 1026    {
 1027       struct in_addr addr4;
 1028       struct in6_addr addr6;
 1029 
 1030       if (inet_pton(AF_INET, tlsFd->hostname.c_str(), &addr4) == 1 ||
 1031       inet_pton(AF_INET6, tlsFd->hostname.c_str(), &addr6) == 1)
 1032      /* not a host name */;
 1033       else if ((err = gnutls_server_name_set(tlsFd->session, GNUTLS_NAME_DNS, tlsFd->hostname.c_str(), tlsFd->hostname.length())) < 0)
 1034       {
 1035      _error->Error("Could not set host name %s to indicate to server: %s", tlsFd->hostname.c_str(), gnutls_strerror(err));
 1036      return ResultState::FATAL_ERROR;
 1037       }
 1038    }
 1039 
 1040    // Set the FD now, so closing it works reliably.
 1041    tlsFd->UnderlyingFd = std::move(Fd);
 1042    Fd.reset(tlsFd);
 1043 
 1044    // Do the handshake.
 1045    err = tlsFd->DoTLSHandshake();
 1046 
 1047    if (err < 0)
 1048       return ResultState::TRANSIENT_ERROR;
 1049 
 1050    return ResultState::SUCCESSFUL;
 1051 }
 1052                                     /*}}}*/