diff options
Diffstat (limited to 'methods')
-rw-r--r-- | methods/CMakeLists.txt | 35 | ||||
-rw-r--r-- | methods/aptmethod.h | 3 | ||||
-rw-r--r-- | methods/basehttp.cc | 39 | ||||
-rw-r--r-- | methods/basehttp.h | 8 | ||||
-rw-r--r-- | methods/cdrom.cc | 4 | ||||
-rw-r--r-- | methods/connect.cc | 509 | ||||
-rw-r--r-- | methods/connect.h | 35 | ||||
-rw-r--r-- | methods/copy.cc | 8 | ||||
-rw-r--r-- | methods/curl.cc (renamed from methods/https.cc) | 18 | ||||
-rw-r--r-- | methods/curl.h (renamed from methods/https.h) | 6 | ||||
-rw-r--r-- | methods/file.cc | 4 | ||||
-rw-r--r-- | methods/ftp.cc | 47 | ||||
-rw-r--r-- | methods/ftp.h | 9 | ||||
-rw-r--r-- | methods/gpgv.cc | 10 | ||||
-rw-r--r-- | methods/http.cc | 431 | ||||
-rw-r--r-- | methods/http.h | 13 | ||||
-rw-r--r-- | methods/http_main.cc | 4 | ||||
-rw-r--r-- | methods/mirror.cc | 46 | ||||
-rw-r--r-- | methods/rfc2553emu.cc | 6 | ||||
-rw-r--r-- | methods/rfc2553emu.h | 2 | ||||
-rw-r--r-- | methods/rred.cc | 14 | ||||
-rw-r--r-- | methods/rsh.cc | 12 | ||||
-rw-r--r-- | methods/store.cc | 8 |
23 files changed, 873 insertions, 398 deletions
diff --git a/methods/CMakeLists.txt b/methods/CMakeLists.txt index a74c2ce07..3ae3f9963 100644 --- a/methods/CMakeLists.txt +++ b/methods/CMakeLists.txt @@ -6,13 +6,18 @@ add_executable(gpgv gpgv.cc) add_executable(cdrom cdrom.cc) add_executable(http http.cc http_main.cc rfc2553emu.cc connect.cc basehttp.cc) add_executable(mirror mirror.cc http.cc rfc2553emu.cc connect.cc basehttp.cc) -add_executable(https https.cc basehttp.cc) +if (HAVE_CURL) + add_executable(curl curl.cc basehttp.cc) +endif() add_executable(ftp ftp.cc rfc2553emu.cc connect.cc) add_executable(rred rred.cc) add_executable(rsh rsh.cc) -# Add target-specific header directories -target_include_directories(https PRIVATE ${CURL_INCLUDE_DIRS}) +target_compile_definitions(http PRIVATE ${GNUTLS_DEFINITIONS}) +target_include_directories(http PRIVATE ${GNUTLS_INCLUDE_DIR}) +if (HAVE_CURL) +target_include_directories(curl PRIVATE ${CURL_INCLUDE_DIRS}) +endif() # Link the executables against the libraries target_link_libraries(file apt-pkg) @@ -20,16 +25,30 @@ target_link_libraries(copy apt-pkg) target_link_libraries(store apt-pkg) target_link_libraries(gpgv apt-pkg) target_link_libraries(cdrom apt-pkg) -target_link_libraries(http apt-pkg) -target_link_libraries(mirror apt-pkg ${RESOLV_LIBRARIES}) -target_link_libraries(https apt-pkg ${CURL_LIBRARIES}) -target_link_libraries(ftp apt-pkg) +target_link_libraries(http apt-pkg ${GNUTLS_LIBRARIES}) +target_link_libraries(mirror apt-pkg ${RESOLV_LIBRARIES} ${GNUTLS_LIBRARIES}) +if (HAVE_CURL) + target_link_libraries(curl apt-pkg ${CURL_LIBRARIES}) +endif() +target_link_libraries(ftp apt-pkg ${GNUTLS_LIBRARIES}) target_link_libraries(rred apt-pkg) target_link_libraries(rsh apt-pkg) # Install the library -install(TARGETS file copy store gpgv cdrom http https ftp rred rsh mirror +install(TARGETS file copy store gpgv cdrom http ftp rred rsh mirror RUNTIME DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/apt/methods) add_slaves(${CMAKE_INSTALL_LIBEXECDIR}/apt/methods store gzip lzma bzip2 xz) add_slaves(${CMAKE_INSTALL_LIBEXECDIR}/apt/methods rsh ssh) + +set(curl_slaves curl+https curl+http) + +if (FORCE_CURL) + set(curl_slaves ${curl_slaves} https) +else() + add_slaves(${CMAKE_INSTALL_LIBEXECDIR}/apt/methods http https) +endif() +if (HAVE_CURL) + install(TARGETS curl RUNTIME DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/apt/methods) + add_slaves(${CMAKE_INSTALL_LIBEXECDIR}/apt/methods curl ${curl_slaves}) +endif() diff --git a/methods/aptmethod.h b/methods/aptmethod.h index 04c4fa99b..04858e29d 100644 --- a/methods/aptmethod.h +++ b/methods/aptmethod.h @@ -4,15 +4,16 @@ #include <apt-pkg/acquire-method.h> #include <apt-pkg/configuration.h> #include <apt-pkg/error.h> +#include <apt-pkg/fileutl.h> #include <algorithm> #include <locale> #include <string> #include <vector> +#include <sys/stat.h> #include <sys/time.h> #include <sys/types.h> -#include <sys/stat.h> #include <unistd.h> #include <apti18n.h> diff --git a/methods/basehttp.cc b/methods/basehttp.cc index d7d9bccd0..c3d570c83 100644 --- a/methods/basehttp.cc +++ b/methods/basehttp.cc @@ -15,6 +15,11 @@ #include <apt-pkg/fileutl.h> #include <apt-pkg/strutl.h> +#include <iostream> +#include <limits> +#include <map> +#include <string> +#include <vector> #include <ctype.h> #include <signal.h> #include <stdio.h> @@ -23,11 +28,6 @@ #include <sys/time.h> #include <time.h> #include <unistd.h> -#include <iostream> -#include <limits> -#include <map> -#include <string> -#include <vector> #include "basehttp.h" @@ -146,6 +146,9 @@ bool RequestState::HeaderLine(string const &Line) /*{{{*/ if (stringcasecmp(Tag,"Content-Length:") == 0) { + auto ContentLength = strtoull(Val.c_str(), NULL, 10); + if (ContentLength == 0) + return true; if (Encoding == Closes) Encoding = Stream; HaveContent = true; @@ -154,7 +157,7 @@ bool RequestState::HeaderLine(string const &Line) /*{{{*/ if (Result == 416 || (Result >= 300 && Result < 400)) DownloadSizePtr = &JunkSize; - *DownloadSizePtr = strtoull(Val.c_str(), NULL, 10); + *DownloadSizePtr = ContentLength; if (*DownloadSizePtr >= std::numeric_limits<unsigned long long>::max()) return _error->Errno("HeaderLine", _("The HTTP server sent an invalid Content-Length header")); else if (*DownloadSizePtr == 0) @@ -286,18 +289,18 @@ BaseHttpMethod::DealWithHeaders(FetchResult &Res, RequestState &Req) return IMS_HIT; } - /* Redirect - * - * Note that it is only OK for us to treat all redirection the same - * because we *always* use GET, not other HTTP methods. There are - * three redirection codes for which it is not appropriate that we - * redirect. Pass on those codes so the error handling kicks in. - */ - if (AllowRedirect - && (Req.Result > 300 && Req.Result < 400) - && (Req.Result != 300 // Multiple Choices - && Req.Result != 304 // Not Modified - && Req.Result != 306)) // (Not part of HTTP/1.1, reserved) + /* Note that it is only OK for us to treat all redirection the same + because we *always* use GET, not other HTTP methods. + Codes not mentioned are handled as errors later as required by the + HTTP spec to handle unknown codes the same as the x00 code. */ + constexpr unsigned int RedirectCodes[] = { + 301, // Moved Permanently + 302, // Found + 303, // See Other + 307, // Temporary Redirect + 308, // Permanent Redirect + }; + if (AllowRedirect && std::find(std::begin(RedirectCodes), std::end(RedirectCodes), Req.Result) != std::end(RedirectCodes)) { if (Req.Location.empty() == true) ; diff --git a/methods/basehttp.h b/methods/basehttp.h index 41a9a4306..7000e7b89 100644 --- a/methods/basehttp.h +++ b/methods/basehttp.h @@ -11,14 +11,14 @@ #ifndef APT_SERVER_H #define APT_SERVER_H -#include <apt-pkg/strutl.h> -#include <apt-pkg/fileutl.h> #include "aptmethod.h" +#include <apt-pkg/fileutl.h> +#include <apt-pkg/strutl.h> -#include <time.h> #include <iostream> -#include <string> #include <memory> +#include <string> +#include <time.h> using std::cout; using std::endl; diff --git a/methods/cdrom.cc b/methods/cdrom.cc index 87a58e948..8b7587fe5 100644 --- a/methods/cdrom.cc +++ b/methods/cdrom.cc @@ -12,11 +12,11 @@ #include <apt-pkg/cdrom.h> #include <apt-pkg/cdromutl.h> -#include <apt-pkg/error.h> #include <apt-pkg/configuration.h> +#include <apt-pkg/error.h> #include <apt-pkg/fileutl.h> -#include <apt-pkg/strutl.h> #include <apt-pkg/hashes.h> +#include <apt-pkg/strutl.h> #include "aptmethod.h" diff --git a/methods/connect.cc b/methods/connect.cc index f6fb14769..949cd3f99 100644 --- a/methods/connect.cc +++ b/methods/connect.cc @@ -13,27 +13,31 @@ // Include Files /*{{{*/ #include <config.h> -#include <apt-pkg/error.h> -#include <apt-pkg/fileutl.h> -#include <apt-pkg/strutl.h> #include <apt-pkg/acquire-method.h> #include <apt-pkg/configuration.h> +#include <apt-pkg/error.h> +#include <apt-pkg/fileutl.h> #include <apt-pkg/srvrec.h> +#include <apt-pkg/strutl.h> -#include <stdio.h> -#include <errno.h> -#include <unistd.h> +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> + +#include <set> #include <sstream> +#include <string> +#include <errno.h> +#include <stdio.h> #include <string.h> -#include<set> -#include<string> +#include <unistd.h> // Internet stuff +#include <netdb.h> +#include <arpa/inet.h> #include <netinet/in.h> #include <sys/socket.h> -#include <arpa/inet.h> -#include <netdb.h> +#include "aptmethod.h" #include "connect.h" #include "rfc2553emu.h" #include <apti18n.h> @@ -76,16 +80,46 @@ static bool ConnectionAllowed(char const * const Service, std::string const &Hos return true; } /*}}}*/ + +// File Descriptor based Fd /*{{{*/ +struct FdFd : public MethodFd +{ + int fd = -1; + int Fd() APT_OVERRIDE { return fd; } + ssize_t Read(void *buf, size_t count) APT_OVERRIDE { return ::read(fd, buf, count); } + ssize_t Write(void *buf, size_t count) APT_OVERRIDE { return ::write(fd, buf, count); } + int Close() APT_OVERRIDE + { + int result = 0; + if (fd != -1) + result = ::close(fd); + fd = -1; + return result; + } +}; + +bool MethodFd::HasPending() +{ + return false; +} +std::unique_ptr<MethodFd> MethodFd::FromFd(int iFd) +{ + FdFd *fd = new FdFd(); + fd->fd = iFd; + return std::unique_ptr<MethodFd>(fd); +} + /*}}}*/ // DoConnect - Attempt a connect operation /*{{{*/ // --------------------------------------------------------------------- /* This helper function attempts a connection to a single address. */ -static bool DoConnect(struct addrinfo *Addr,std::string const &Host, - unsigned long TimeOut,int &Fd,pkgAcqMethod *Owner) +static bool DoConnect(struct addrinfo *Addr, std::string const &Host, + unsigned long TimeOut, std::unique_ptr<MethodFd> &Fd, aptMethod *Owner) { // Show a status indicator char Name[NI_MAXHOST]; char Service[NI_MAXSERV]; - + Fd.reset(new FdFd()); + Name[0] = 0; Service[0] = 0; getnameinfo(Addr->ai_addr,Addr->ai_addrlen, @@ -107,20 +141,21 @@ static bool DoConnect(struct addrinfo *Addr,std::string const &Host, } // Get a socket - if ((Fd = socket(Addr->ai_family,Addr->ai_socktype, - Addr->ai_protocol)) < 0) + if ((static_cast<FdFd *>(Fd.get())->fd = socket(Addr->ai_family, Addr->ai_socktype, + Addr->ai_protocol)) < 0) return _error->Errno("socket",_("Could not create a socket for %s (f=%u t=%u p=%u)"), Name,Addr->ai_family,Addr->ai_socktype,Addr->ai_protocol); - - SetNonBlock(Fd,true); - if (connect(Fd,Addr->ai_addr,Addr->ai_addrlen) < 0 && + + SetNonBlock(Fd->Fd(), true); + if (connect(Fd->Fd(), Addr->ai_addr, Addr->ai_addrlen) < 0 && errno != EINPROGRESS) return _error->Errno("connect",_("Cannot initiate the connection " "to %s:%s (%s)."),Host.c_str(),Service,Name); /* This implements a timeout for connect by opening the connection nonblocking */ - if (WaitFd(Fd,true,TimeOut) == false) { + if (WaitFd(Fd->Fd(), true, TimeOut) == false) + { bad_addr.insert(bad_addr.begin(), std::string(Name)); Owner->SetFailReason("Timeout"); return _error->Error(_("Could not connect to %s:%s (%s), " @@ -130,7 +165,7 @@ static bool DoConnect(struct addrinfo *Addr,std::string const &Host, // Check the socket for an error condition unsigned int Err; unsigned int Len = sizeof(Err); - if (getsockopt(Fd,SOL_SOCKET,SO_ERROR,&Err,&Len) != 0) + if (getsockopt(Fd->Fd(), SOL_SOCKET, SO_ERROR, &Err, &Len) != 0) return _error->Errno("getsockopt",_("Failed")); if (Err != 0) @@ -144,14 +179,16 @@ static bool DoConnect(struct addrinfo *Addr,std::string const &Host, return _error->Errno("connect",_("Could not connect to %s:%s (%s)."),Host.c_str(), Service,Name); } - + + Owner->SetFailReason(""); + return true; } /*}}}*/ // Connect to a given Hostname /*{{{*/ static bool ConnectToHostname(std::string const &Host, int const Port, - const char * const Service, int DefPort, int &Fd, - unsigned long const TimeOut, pkgAcqMethod * const Owner) + const char *const Service, int DefPort, std::unique_ptr<MethodFd> &Fd, + unsigned long const TimeOut, aptMethod *const Owner) { if (ConnectionAllowed(Service, Host) == false) return false; @@ -254,10 +291,9 @@ static bool ConnectToHostname(std::string const &Host, int const Port, { LastUsed = CurHost; return true; - } - close(Fd); - Fd = -1; - + } + Fd->Close(); + // Ignore UNIX domain sockets do { @@ -286,9 +322,9 @@ static bool ConnectToHostname(std::string const &Host, int const Port, // Connect - Connect to a server /*{{{*/ // --------------------------------------------------------------------- /* Performs a connection to the server (including SRV record lookup) */ -bool Connect(std::string Host,int Port,const char *Service, - int DefPort,int &Fd, - unsigned long TimeOut,pkgAcqMethod *Owner) +bool Connect(std::string Host, int Port, const char *Service, + int DefPort, std::unique_ptr<MethodFd> &Fd, + unsigned long TimeOut, aptMethod *Owner) { if (_error->PendingError() == true) return false; @@ -312,12 +348,15 @@ bool Connect(std::string Host,int Port,const char *Service, size_t stackSize = 0; // try to connect in the priority order of the srv records std::string initialHost{std::move(Host)}; + auto const initialPort = Port; while(SrvRecords.empty() == false) { _error->PushToStack(); ++stackSize; // PopFromSrvRecs will also remove the server - Host = PopFromSrvRecs(SrvRecords).target; + auto Srv = PopFromSrvRecs(SrvRecords); + Host = Srv.target; + Port = Srv.port; auto const ret = ConnectToHostname(Host, Port, Service, DefPort, Fd, TimeOut, Owner); if (ret) { @@ -327,6 +366,7 @@ bool Connect(std::string Host,int Port,const char *Service, } } Host = std::move(initialHost); + Port = initialPort; // we have no (good) SrvRecords for this host, connect right away _error->PushToStack(); @@ -340,3 +380,410 @@ bool Connect(std::string Host,int Port,const char *Service, _error->MergeWithStack(); return ret; } + /*}}}*/ +// UnwrapSocks - Handle SOCKS setup /*{{{*/ +// --------------------------------------------------------------------- +/* This does socks magic */ +static bool TalkToSocksProxy(int const ServerFd, std::string const &Proxy, + char const *const type, bool const ReadWrite, uint8_t *const ToFrom, + unsigned int const Size, unsigned int const Timeout) +{ + if (WaitFd(ServerFd, ReadWrite, Timeout) == false) + return _error->Error("Waiting for the SOCKS proxy %s to %s timed out", URI::SiteOnly(Proxy).c_str(), type); + if (ReadWrite == false) + { + if (FileFd::Read(ServerFd, ToFrom, Size) == false) + return _error->Error("Reading the %s from SOCKS proxy %s failed", type, URI::SiteOnly(Proxy).c_str()); + } + else + { + if (FileFd::Write(ServerFd, ToFrom, Size) == false) + return _error->Error("Writing the %s to SOCKS proxy %s failed", type, URI::SiteOnly(Proxy).c_str()); + } + return true; +} + +bool UnwrapSocks(std::string Host, int Port, URI Proxy, std::unique_ptr<MethodFd> &Fd, + unsigned long Timeout, aptMethod *Owner) +{ + /* We implement a very basic SOCKS5 client here complying mostly to RFC1928 expect + * for not offering GSSAPI auth which is a must (we only do no or user/pass auth). + * We also expect the SOCKS5 server to do hostname lookup (aka socks5h) */ + std::string const ProxyInfo = URI::SiteOnly(Proxy); + Owner->Status(_("Connecting to %s (%s)"), "SOCKS5h proxy", ProxyInfo.c_str()); +#define APT_WriteOrFail(TYPE, DATA, LENGTH) \ + if (TalkToSocksProxy(Fd->Fd(), ProxyInfo, TYPE, true, DATA, LENGTH, Timeout) == false) \ + return false +#define APT_ReadOrFail(TYPE, DATA, LENGTH) \ + if (TalkToSocksProxy(Fd->Fd(), ProxyInfo, TYPE, false, DATA, LENGTH, Timeout) == false) \ + return false + if (Host.length() > 255) + return _error->Error("Can't use SOCKS5h as hostname %s is too long!", Host.c_str()); + if (Proxy.User.length() > 255 || Proxy.Password.length() > 255) + return _error->Error("Can't use user&pass auth as they are too long (%lu and %lu) for the SOCKS5!", Proxy.User.length(), Proxy.Password.length()); + if (Proxy.User.empty()) + { + uint8_t greeting[] = {0x05, 0x01, 0x00}; + APT_WriteOrFail("greet-1", greeting, sizeof(greeting)); + } + else + { + uint8_t greeting[] = {0x05, 0x02, 0x00, 0x02}; + APT_WriteOrFail("greet-2", greeting, sizeof(greeting)); + } + uint8_t greeting[2]; + APT_ReadOrFail("greet back", greeting, sizeof(greeting)); + if (greeting[0] != 0x05) + return _error->Error("SOCKS proxy %s greets back with wrong version: %d", ProxyInfo.c_str(), greeting[0]); + if (greeting[1] == 0x00) + ; // no auth has no method-dependent sub-negotiations + else if (greeting[1] == 0x02) + { + if (Proxy.User.empty()) + return _error->Error("SOCKS proxy %s negotiated user&pass auth, but we had not offered it!", ProxyInfo.c_str()); + // user&pass auth sub-negotiations are defined by RFC1929 + std::vector<uint8_t> auth = {{0x01, static_cast<uint8_t>(Proxy.User.length())}}; + std::copy(Proxy.User.begin(), Proxy.User.end(), std::back_inserter(auth)); + auth.push_back(static_cast<uint8_t>(Proxy.Password.length())); + std::copy(Proxy.Password.begin(), Proxy.Password.end(), std::back_inserter(auth)); + APT_WriteOrFail("user&pass auth", auth.data(), auth.size()); + uint8_t authstatus[2]; + APT_ReadOrFail("auth report", authstatus, sizeof(authstatus)); + if (authstatus[0] != 0x01) + return _error->Error("SOCKS proxy %s auth status response with wrong version: %d", ProxyInfo.c_str(), authstatus[0]); + if (authstatus[1] != 0x00) + return _error->Error("SOCKS proxy %s reported authorization failure: username or password incorrect? (%d)", ProxyInfo.c_str(), authstatus[1]); + } + else + return _error->Error("SOCKS proxy %s greets back having not found a common authorization method: %d", ProxyInfo.c_str(), greeting[1]); + union { + uint16_t *i; + uint8_t *b; + } portu; + uint16_t port = htons(static_cast<uint16_t>(Port)); + portu.i = &port; + std::vector<uint8_t> request = {{0x05, 0x01, 0x00, 0x03, static_cast<uint8_t>(Host.length())}}; + std::copy(Host.begin(), Host.end(), std::back_inserter(request)); + request.push_back(portu.b[0]); + request.push_back(portu.b[1]); + APT_WriteOrFail("request", request.data(), request.size()); + uint8_t response[4]; + APT_ReadOrFail("first part of response", response, sizeof(response)); + if (response[0] != 0x05) + return _error->Error("SOCKS proxy %s response with wrong version: %d", ProxyInfo.c_str(), response[0]); + if (response[2] != 0x00) + return _error->Error("SOCKS proxy %s has unexpected non-zero reserved field value: %d", ProxyInfo.c_str(), response[2]); + std::string bindaddr; + if (response[3] == 0x01) // IPv4 address + { + uint8_t ip4port[6]; + APT_ReadOrFail("IPv4+Port of response", ip4port, sizeof(ip4port)); + portu.b[0] = ip4port[4]; + portu.b[1] = ip4port[5]; + port = ntohs(*portu.i); + strprintf(bindaddr, "%d.%d.%d.%d:%d", ip4port[0], ip4port[1], ip4port[2], ip4port[3], port); + } + else if (response[3] == 0x03) // hostname + { + uint8_t namelength; + APT_ReadOrFail("hostname length of response", &namelength, 1); + uint8_t hostname[namelength + 2]; + APT_ReadOrFail("hostname of response", hostname, sizeof(hostname)); + portu.b[0] = hostname[namelength]; + portu.b[1] = hostname[namelength + 1]; + port = ntohs(*portu.i); + hostname[namelength] = '\0'; + strprintf(bindaddr, "%s:%d", hostname, port); + } + else if (response[3] == 0x04) // IPv6 address + { + uint8_t ip6port[18]; + APT_ReadOrFail("IPv6+port of response", ip6port, sizeof(ip6port)); + portu.b[0] = ip6port[16]; + portu.b[1] = ip6port[17]; + port = ntohs(*portu.i); + strprintf(bindaddr, "[%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X]:%d", + ip6port[0], ip6port[1], ip6port[2], ip6port[3], ip6port[4], ip6port[5], ip6port[6], ip6port[7], + ip6port[8], ip6port[9], ip6port[10], ip6port[11], ip6port[12], ip6port[13], ip6port[14], ip6port[15], + port); + } + else + return _error->Error("SOCKS proxy %s destination address is of unknown type: %d", + ProxyInfo.c_str(), response[3]); + if (response[1] != 0x00) + { + char const *errstr = nullptr; + auto errcode = response[1]; + // Tor error reporting can be a bit arcane, lets try to detect & fix it up + if (bindaddr == "0.0.0.0:0") + { + auto const lastdot = Host.rfind('.'); + if (lastdot == std::string::npos || Host.substr(lastdot) != ".onion") + ; + else if (errcode == 0x01) + { + auto const prevdot = Host.rfind('.', lastdot - 1); + if (lastdot == 16 && prevdot == std::string::npos) + ; // valid .onion address + else if (prevdot != std::string::npos && (lastdot - prevdot) == 17) + ; // valid .onion address with subdomain(s) + else + { + errstr = "Invalid hostname: onion service name must be 16 characters long"; + Owner->SetFailReason("SOCKS"); + } + } + // in all likelihood the service is either down or the address has + // a typo and so "Host unreachable" is the better understood error + // compared to the technically correct "TLL expired". + else if (errcode == 0x06) + errcode = 0x04; + } + if (errstr == nullptr) + { + switch (errcode) + { + case 0x01: + errstr = "general SOCKS server failure"; + Owner->SetFailReason("SOCKS"); + break; + case 0x02: + errstr = "connection not allowed by ruleset"; + Owner->SetFailReason("SOCKS"); + break; + case 0x03: + errstr = "Network unreachable"; + Owner->SetFailReason("ConnectionTimedOut"); + break; + case 0x04: + errstr = "Host unreachable"; + Owner->SetFailReason("ConnectionTimedOut"); + break; + case 0x05: + errstr = "Connection refused"; + Owner->SetFailReason("ConnectionRefused"); + break; + case 0x06: + errstr = "TTL expired"; + Owner->SetFailReason("Timeout"); + break; + case 0x07: + errstr = "Command not supported"; + Owner->SetFailReason("SOCKS"); + break; + case 0x08: + errstr = "Address type not supported"; + Owner->SetFailReason("SOCKS"); + break; + default: + errstr = "Unknown error"; + Owner->SetFailReason("SOCKS"); + break; + } + } + return _error->Error("SOCKS proxy %s could not connect to %s (%s) due to: %s (%d)", + ProxyInfo.c_str(), Host.c_str(), bindaddr.c_str(), errstr, response[1]); + } + else if (Owner->DebugEnabled()) + ioprintf(std::clog, "http: SOCKS proxy %s connection established to %s (%s)\n", + ProxyInfo.c_str(), Host.c_str(), bindaddr.c_str()); + + if (WaitFd(Fd->Fd(), true, Timeout) == false) + return _error->Error("SOCKS proxy %s reported connection to %s (%s), but timed out", + ProxyInfo.c_str(), Host.c_str(), bindaddr.c_str()); +#undef APT_ReadOrFail +#undef APT_WriteOrFail + + return true; +} + /*}}}*/ +// UnwrapTLS - Handle TLS connections /*{{{*/ +// --------------------------------------------------------------------- +/* Performs a TLS handshake on the socket */ +struct TlsFd : public MethodFd +{ + std::unique_ptr<MethodFd> UnderlyingFd; + gnutls_session_t session; + gnutls_certificate_credentials_t credentials; + std::string hostname; + + int Fd() APT_OVERRIDE { return UnderlyingFd->Fd(); } + + ssize_t Read(void *buf, size_t count) APT_OVERRIDE + { + return HandleError(gnutls_record_recv(session, buf, count)); + } + ssize_t Write(void *buf, size_t count) APT_OVERRIDE + { + return HandleError(gnutls_record_send(session, buf, count)); + } + + template <typename T> + T HandleError(T err) + { + if (err < 0 && gnutls_error_is_fatal(err)) + errno = EIO; + else if (err < 0) + errno = EAGAIN; + else + errno = 0; + return err; + } + + int Close() APT_OVERRIDE + { + auto err = HandleError(gnutls_bye(session, GNUTLS_SHUT_RDWR)); + auto lower = UnderlyingFd->Close(); + return err < 0 ? HandleError(err) : lower; + } + + bool HasPending() APT_OVERRIDE + { + return gnutls_record_check_pending(session) > 0; + } +}; + +bool UnwrapTLS(std::string Host, std::unique_ptr<MethodFd> &Fd, + unsigned long Timeout, aptMethod *Owner) +{ + if (_config->FindB("Acquire::AllowTLS", true) == false) + return _error->Error("TLS support has been disabled: Acquire::AllowTLS is false."); + + int err; + TlsFd *tlsFd = new TlsFd(); + + tlsFd->hostname = Host; + tlsFd->UnderlyingFd = MethodFd::FromFd(-1); // For now + + if ((err = gnutls_init(&tlsFd->session, GNUTLS_CLIENT | GNUTLS_NONBLOCK)) < 0) + return _error->Error("Internal error: could not allocate credentials: %s", gnutls_strerror(err)); + + FdFd *fdfd = dynamic_cast<FdFd *>(Fd.get()); + if (fdfd != nullptr) + { + gnutls_transport_set_int(tlsFd->session, fdfd->fd); + } + else + { + gnutls_transport_set_ptr(tlsFd->session, Fd.get()); + gnutls_transport_set_pull_function(tlsFd->session, + [](gnutls_transport_ptr_t p, void *buf, size_t size) -> ssize_t { + return reinterpret_cast<MethodFd *>(p)->Read(buf, size); + }); + gnutls_transport_set_push_function(tlsFd->session, + [](gnutls_transport_ptr_t p, const void *buf, size_t size) -> ssize_t { + return reinterpret_cast<MethodFd *>(p)->Write((void *)buf, size); + }); + } + + if ((err = gnutls_certificate_allocate_credentials(&tlsFd->credentials)) < 0) + return _error->Error("Internal error: could not allocate credentials: %s", gnutls_strerror(err)); + + // Credential setup + std::string fileinfo = Owner->ConfigFind("CaInfo", ""); + if (fileinfo.empty()) + { + // No CaInfo specified, use system trust store. + err = gnutls_certificate_set_x509_system_trust(tlsFd->credentials); + if (err == 0) + Owner->Warning("No system certificates available. Try installing ca-certificates."); + else if (err < 0) + return _error->Error("Could not load system TLS certificates: %s", gnutls_strerror(err)); + } + else + { + // CA location has been set, use the specified one instead + gnutls_certificate_set_verify_flags(tlsFd->credentials, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT); + err = gnutls_certificate_set_x509_trust_file(tlsFd->credentials, fileinfo.c_str(), GNUTLS_X509_FMT_PEM); + if (err < 0) + return _error->Error("Could not load certificates from %s (CaInfo option): %s", fileinfo.c_str(), gnutls_strerror(err)); + } + + if (!Owner->ConfigFind("IssuerCert", "").empty()) + return _error->Error("The option '%s' is not supported anymore", "IssuerCert"); + if (!Owner->ConfigFind("SslForceVersion", "").empty()) + return _error->Error("The option '%s' is not supported anymore", "SslForceVersion"); + + // For client authentication, certificate file ... + std::string const cert = Owner->ConfigFind("SslCert", ""); + std::string const key = Owner->ConfigFind("SslKey", ""); + if (cert.empty() == false) + { + if ((err = gnutls_certificate_set_x509_key_file( + tlsFd->credentials, + cert.c_str(), + key.empty() ? cert.c_str() : key.c_str(), + GNUTLS_X509_FMT_PEM)) < 0) + { + return _error->Error("Could not load client certificate (%s, SslCert option) or key (%s, SslKey option): %s", cert.c_str(), key.c_str(), gnutls_strerror(err)); + } + } + + // CRL file + std::string const crlfile = Owner->ConfigFind("CrlFile", ""); + if (crlfile.empty() == false) + { + if ((err = gnutls_certificate_set_x509_crl_file(tlsFd->credentials, + crlfile.c_str(), + GNUTLS_X509_FMT_PEM)) < 0) + return _error->Error("Could not load custom certificate revocation list %s (CrlFile option): %s", crlfile.c_str(), gnutls_strerror(err)); + } + + if ((err = gnutls_credentials_set(tlsFd->session, GNUTLS_CRD_CERTIFICATE, tlsFd->credentials)) < 0) + return _error->Error("Internal error: Could not add certificates to session: %s", gnutls_strerror(err)); + + if ((err = gnutls_set_default_priority(tlsFd->session)) < 0) + return _error->Error("Internal error: Could not set algorithm preferences: %s", gnutls_strerror(err)); + + if (Owner->ConfigFindB("Verify-Peer", true)) + { + gnutls_session_set_verify_cert(tlsFd->session, Owner->ConfigFindB("Verify-Host", true) ? tlsFd->hostname.c_str() : nullptr, 0); + } + + // set SNI only if the hostname is really a name and not an address + { + struct in_addr addr4; + struct in6_addr addr6; + + if (inet_pton(AF_INET, tlsFd->hostname.c_str(), &addr4) == 1 || + inet_pton(AF_INET6, tlsFd->hostname.c_str(), &addr6) == 1) + /* not a host name */; + else if ((err = gnutls_server_name_set(tlsFd->session, GNUTLS_NAME_DNS, tlsFd->hostname.c_str(), tlsFd->hostname.length())) < 0) + return _error->Error("Could not set host name %s to indicate to server: %s", tlsFd->hostname.c_str(), gnutls_strerror(err)); + } + + // Set the FD now, so closing it works reliably. + tlsFd->UnderlyingFd = std::move(Fd); + Fd.reset(tlsFd); + + // Do the handshake. Our socket is non-blocking, so we need to call WaitFd() + // accordingly. + do + { + err = gnutls_handshake(tlsFd->session); + if ((err == GNUTLS_E_INTERRUPTED || err == GNUTLS_E_AGAIN) && + WaitFd(Fd->Fd(), gnutls_record_get_direction(tlsFd->session) == 1, Timeout) == false) + return _error->Errno("select", "Could not wait for server fd"); + } while (err < 0 && gnutls_error_is_fatal(err) == 0); + + if (err < 0) + { + // Print reason why validation failed. + if (err == GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR) + { + gnutls_datum_t txt; + auto type = gnutls_certificate_type_get(tlsFd->session); + auto status = gnutls_session_get_verify_cert_status(tlsFd->session); + if (gnutls_certificate_verification_status_print(status, + type, &txt, 0) == 0) + { + _error->Error("Certificate verification failed: %s", txt.data); + } + gnutls_free(txt.data); + } + return _error->Error("Could not handshake: %s", gnutls_strerror(err)); + } + + return true; +} + /*}}}*/ diff --git a/methods/connect.h b/methods/connect.h index bbe1bb35d..817ebf765 100644 --- a/methods/connect.h +++ b/methods/connect.h @@ -10,12 +10,41 @@ #ifndef CONNECT_H #define CONNECT_H +#include <memory> #include <string> +#include <stddef.h> -class pkgAcqMethod; +class aptMethod; + +/** + * \brief Small representation of a file descriptor for network traffic. + * + * This provides support for TLS, SOCKS, and HTTP CONNECT proxies. + */ +struct MethodFd +{ + /// \brief Returns -1 for unusable, or an fd to select() on otherwise + virtual int Fd() = 0; + /// \brief Should behave like read(2) + virtual ssize_t Read(void *buf, size_t count) = 0; + /// \brief Should behave like write(2) + virtual ssize_t Write(void *buf, size_t count) = 0; + /// \brief Closes the file descriptor. Can be called multiple times. + virtual int Close() = 0; + /// \brief Destructor + virtual ~MethodFd(){}; + /// \brief Construct a MethodFd from a UNIX file descriptor + static std::unique_ptr<MethodFd> FromFd(int iFd); + /// \brief If there is pending data. + virtual bool HasPending(); +}; + +bool Connect(std::string To, int Port, const char *Service, int DefPort, + std::unique_ptr<MethodFd> &Fd, unsigned long TimeOut, aptMethod *Owner); + +bool UnwrapSocks(std::string To, int Port, URI Proxy, std::unique_ptr<MethodFd> &Fd, unsigned long Timeout, aptMethod *Owner); +bool UnwrapTLS(std::string To, std::unique_ptr<MethodFd> &Fd, unsigned long Timeout, aptMethod *Owner); -bool Connect(std::string To,int Port,const char *Service,int DefPort, - int &Fd,unsigned long TimeOut,pkgAcqMethod *Owner); void RotateDNS(); #endif diff --git a/methods/copy.cc b/methods/copy.cc index 810fc2f38..fd4786ede 100644 --- a/methods/copy.cc +++ b/methods/copy.cc @@ -11,12 +11,12 @@ // Include Files /*{{{*/ #include <config.h> -#include <apt-pkg/fileutl.h> -#include <apt-pkg/strutl.h> +#include "aptmethod.h" +#include <apt-pkg/configuration.h> #include <apt-pkg/error.h> +#include <apt-pkg/fileutl.h> #include <apt-pkg/hashes.h> -#include <apt-pkg/configuration.h> -#include "aptmethod.h" +#include <apt-pkg/strutl.h> #include <string> #include <sys/stat.h> diff --git a/methods/https.cc b/methods/curl.cc index d71ef0bf0..dfdd3b06b 100644 --- a/methods/https.cc +++ b/methods/curl.cc @@ -12,28 +12,27 @@ // Include Files /*{{{*/ #include <config.h> -#include <apt-pkg/fileutl.h> +#include <apt-pkg/configuration.h> #include <apt-pkg/error.h> +#include <apt-pkg/fileutl.h> #include <apt-pkg/hashes.h> -#include <apt-pkg/netrc.h> -#include <apt-pkg/configuration.h> #include <apt-pkg/macros.h> -#include <apt-pkg/strutl.h> +#include <apt-pkg/netrc.h> #include <apt-pkg/proxy.h> +#include <apt-pkg/strutl.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> #include <sys/stat.h> #include <sys/time.h> #include <unistd.h> -#include <stdio.h> -#include <ctype.h> -#include <stdlib.h> #include <array> #include <iostream> #include <sstream> - -#include "https.h" +#include "curl.h" #include <apti18n.h> /*}}}*/ @@ -469,6 +468,7 @@ bool HttpsMethod::Fetch(FetchItem *Itm) case BaseHttpMethod::ERROR_WITH_CONTENT_PAGE: // unlink, no need keep 401/404 page content in partial/ RemoveFile(Binary.c_str(), Req.File.Name()); + // Fall through. case BaseHttpMethod::ERROR_UNRECOVERABLE: case BaseHttpMethod::ERROR_NOT_FROM_SERVER: return false; diff --git a/methods/https.h b/methods/curl.h index fbbf34501..d352dc52a 100644 --- a/methods/https.h +++ b/methods/curl.h @@ -11,11 +11,11 @@ #ifndef APT_HTTPS_H #define APT_HTTPS_H -#include <curl/curl.h> #include <iostream> -#include <stddef.h> -#include <string> #include <memory> +#include <string> +#include <curl/curl.h> +#include <stddef.h> #include "basehttp.h" diff --git a/methods/file.cc b/methods/file.cc index 5cbf1924e..6111329d5 100644 --- a/methods/file.cc +++ b/methods/file.cc @@ -15,12 +15,12 @@ // Include Files /*{{{*/ #include <config.h> +#include "aptmethod.h" #include <apt-pkg/aptconfiguration.h> #include <apt-pkg/error.h> -#include <apt-pkg/hashes.h> #include <apt-pkg/fileutl.h> +#include <apt-pkg/hashes.h> #include <apt-pkg/strutl.h> -#include "aptmethod.h" #include <string> #include <sys/stat.h> diff --git a/methods/ftp.cc b/methods/ftp.cc index d789637a8..9bfe72bc6 100644 --- a/methods/ftp.cc +++ b/methods/ftp.cc @@ -17,33 +17,33 @@ // Include Files /*{{{*/ #include <config.h> -#include <apt-pkg/fileutl.h> +#include <apt-pkg/configuration.h> #include <apt-pkg/error.h> +#include <apt-pkg/fileutl.h> #include <apt-pkg/hashes.h> #include <apt-pkg/netrc.h> -#include <apt-pkg/configuration.h> #include <apt-pkg/strutl.h> +#include <iostream> #include <ctype.h> +#include <errno.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <unistd.h> -#include <signal.h> -#include <stdio.h> -#include <errno.h> -#include <stdarg.h> -#include <iostream> // Internet stuff -#include <netinet/in.h> -#include <arpa/inet.h> #include <netdb.h> +#include <arpa/inet.h> +#include <netinet/in.h> -#include "rfc2553emu.h" #include "connect.h" #include "ftp.h" +#include "rfc2553emu.h" #include <apti18n.h> /*}}}*/ @@ -73,8 +73,8 @@ time_t FtpMethod::FailTime = 0; // FTPConn::FTPConn - Constructor /*{{{*/ // --------------------------------------------------------------------- /* */ -FTPConn::FTPConn(URI Srv) : Len(0), ServerFd(-1), DataFd(-1), - DataListenFd(-1), ServerName(Srv), +FTPConn::FTPConn(URI Srv) : Len(0), ServerFd(MethodFd::FromFd(-1)), DataFd(-1), + DataListenFd(-1), ServerName(Srv), ForceExtended(false), TryPassive(true), PeerAddrLen(0), ServerAddrLen(0) { @@ -96,8 +96,7 @@ FTPConn::~FTPConn() /* Just tear down the socket and data socket */ void FTPConn::Close() { - close(ServerFd); - ServerFd = -1; + ServerFd->Close(); close(DataFd); DataFd = -1; close(DataListenFd); @@ -112,10 +111,10 @@ void FTPConn::Close() // --------------------------------------------------------------------- /* Connect to the server using a non-blocking connection and perform a login. */ -bool FTPConn::Open(pkgAcqMethod *Owner) +bool FTPConn::Open(aptMethod *Owner) { // Use the already open connection if possible. - if (ServerFd != -1) + if (ServerFd->Fd() != -1) return true; Close(); @@ -178,12 +177,12 @@ bool FTPConn::Open(pkgAcqMethod *Owner) // Get the remote server's address PeerAddrLen = sizeof(PeerAddr); - if (getpeername(ServerFd,(sockaddr *)&PeerAddr,&PeerAddrLen) != 0) + if (getpeername(ServerFd->Fd(), (sockaddr *)&PeerAddr, &PeerAddrLen) != 0) return _error->Errno("getpeername",_("Unable to determine the peer name")); // Get the local machine's address ServerAddrLen = sizeof(ServerAddr); - if (getsockname(ServerFd,(sockaddr *)&ServerAddr,&ServerAddrLen) != 0) + if (getsockname(ServerFd->Fd(), (sockaddr *)&ServerAddr, &ServerAddrLen) != 0) return _error->Errno("getsockname",_("Unable to determine the local name")); return Res; @@ -314,7 +313,7 @@ bool FTPConn::Login() /* This performs a very simple buffered read. */ bool FTPConn::ReadLine(string &Text) { - if (ServerFd == -1) + if (ServerFd->Fd() == -1) return false; // Suck in a line @@ -339,14 +338,14 @@ bool FTPConn::ReadLine(string &Text) } // Wait for some data.. - if (WaitFd(ServerFd,false,TimeOut) == false) + if (WaitFd(ServerFd->Fd(), false, TimeOut) == false) { Close(); return _error->Error(_("Connection timeout")); } // Suck it back - int Res = read(ServerFd,Buffer + Len,sizeof(Buffer) - Len); + int Res = ServerFd->Read(Buffer + Len, sizeof(Buffer) - Len); if (Res == 0) _error->Error(_("Server closed the connection")); if (Res <= 0) @@ -451,13 +450,13 @@ bool FTPConn::WriteMsg(unsigned int &Ret,string &Text,const char *Fmt,...) unsigned long Start = 0; while (Len != 0) { - if (WaitFd(ServerFd,true,TimeOut) == false) + if (WaitFd(ServerFd->Fd(), true, TimeOut) == false) { Close(); return _error->Error(_("Connection timeout")); } - - int Res = write(ServerFd,S + Start,Len); + + int Res = ServerFd->Write(S + Start, Len); if (Res <= 0) { _error->Errno("write",_("Write error")); diff --git a/methods/ftp.h b/methods/ftp.h index 6a12475a0..67d00d9f1 100644 --- a/methods/ftp.h +++ b/methods/ftp.h @@ -10,19 +10,20 @@ #ifndef APT_FTP_H #define APT_FTP_H -#include <apt-pkg/strutl.h> #include "aptmethod.h" +#include "connect.h" +#include <apt-pkg/strutl.h> +#include <string> #include <sys/socket.h> #include <sys/types.h> #include <time.h> -#include <string> class FTPConn { char Buffer[1024*10]; unsigned long Len; - int ServerFd; + std::unique_ptr<MethodFd> ServerFd; int DataFd; int DataListenFd; URI ServerName; @@ -55,7 +56,7 @@ class FTPConn bool WriteMsg(unsigned int &Ret,std::string &Text,const char *Fmt,...); // Connection control - bool Open(pkgAcqMethod *Owner); + bool Open(aptMethod *Owner); void Close(); bool GoPasv(); bool ExtGoPasv(); diff --git a/methods/gpgv.cc b/methods/gpgv.cc index 51c268d02..8de15c48a 100644 --- a/methods/gpgv.cc +++ b/methods/gpgv.cc @@ -1,11 +1,11 @@ #include <config.h> +#include "aptmethod.h" #include <apt-pkg/configuration.h> #include <apt-pkg/error.h> +#include <apt-pkg/fileutl.h> #include <apt-pkg/gpgv.h> #include <apt-pkg/strutl.h> -#include <apt-pkg/fileutl.h> -#include "aptmethod.h" #include <ctype.h> #include <errno.h> @@ -16,11 +16,11 @@ #include <sys/wait.h> #include <unistd.h> -#include <array> #include <algorithm> -#include <sstream> -#include <iterator> +#include <array> #include <iostream> +#include <iterator> +#include <sstream> #include <string> #include <vector> diff --git a/methods/http.cc b/methods/http.cc index 9f5959548..9f01d15b3 100644 --- a/methods/http.cc +++ b/methods/http.cc @@ -13,40 +13,32 @@ socket. This provides ideal pipelining as in many cases all of the requests will fit into a single packet. The input socket is buffered the same way and fed into the fd for the file (may be a pipe in future). - - This double buffering provides fairly substantial transfer rates, - compared to wget the http method is about 4% faster. Most importantly, - when HTTP is compared with FTP as a protocol the speed difference is - huge. In tests over the internet from two sites to llug (via ATM) this - program got 230k/s sustained http transfer rates. FTP on the other - hand topped out at 170k/s. That combined with the time to setup the - FTP connection makes HTTP a vastly superior protocol. ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ #include <config.h> -#include <apt-pkg/fileutl.h> #include <apt-pkg/configuration.h> #include <apt-pkg/error.h> +#include <apt-pkg/fileutl.h> #include <apt-pkg/hashes.h> #include <apt-pkg/netrc.h> -#include <apt-pkg/strutl.h> #include <apt-pkg/proxy.h> +#include <apt-pkg/strutl.h> +#include <cstring> +#include <iostream> +#include <sstream> +#include <arpa/inet.h> +#include <errno.h> #include <stddef.h> +#include <stdio.h> #include <stdlib.h> #include <sys/select.h> -#include <cstring> #include <sys/stat.h> #include <sys/time.h> #include <unistd.h> -#include <stdio.h> -#include <errno.h> -#include <arpa/inet.h> -#include <iostream> -#include <sstream> #include "config.h" #include "connect.h" @@ -95,7 +87,7 @@ void CircleBuf::Reset() // --------------------------------------------------------------------- /* This fills up the buffer with as much data as is in the FD, assuming it is non-blocking.. */ -bool CircleBuf::Read(int Fd) +bool CircleBuf::Read(std::unique_ptr<MethodFd> const &Fd) { while (1) { @@ -126,11 +118,11 @@ bool CircleBuf::Read(int Fd) // Write the buffer segment ssize_t Res; if(CircleBuf::BwReadLimit) { - Res = read(Fd,Buf + (InP%Size), - BwReadMax > LeftRead() ? LeftRead() : BwReadMax); + Res = Fd->Read(Buf + (InP % Size), + BwReadMax > LeftRead() ? LeftRead() : BwReadMax); } else - Res = read(Fd,Buf + (InP%Size),LeftRead()); - + Res = Fd->Read(Buf + (InP % Size), LeftRead()); + if(Res > 0 && BwReadLimit > 0) CircleBuf::BwTickReadData += Res; @@ -193,7 +185,7 @@ void CircleBuf::FillOut() // CircleBuf::Write - Write from the buffer into a FD /*{{{*/ // --------------------------------------------------------------------- /* This empties the buffer into the FD. */ -bool CircleBuf::Write(int Fd) +bool CircleBuf::Write(std::unique_ptr<MethodFd> const &Fd) { while (1) { @@ -208,7 +200,7 @@ bool CircleBuf::Write(int Fd) // Write the buffer segment ssize_t Res; - Res = write(Fd,Buf + (OutP%Size),LeftWrite()); + Res = Fd->Write(Buf + (OutP % Size), LeftWrite()); if (Res == 0) return false; @@ -266,6 +258,15 @@ bool CircleBuf::WriteTillEl(string &Data,bool Single) return false; } /*}}}*/ +// CircleBuf::Write - Write from the buffer to a string /*{{{*/ +// --------------------------------------------------------------------- +/* This copies everything */ +bool CircleBuf::Write(string &Data) +{ + Data = std::string((char *)Buf + (OutP % Size), LeftWrite()); + OutP += LeftWrite(); + return true; +} // CircleBuf::Stats - Print out stats information /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -287,38 +288,137 @@ CircleBuf::~CircleBuf() delete Hash; } +// UnwrapHTTPConnect - Does the HTTP CONNECT handshake /*{{{*/ +// --------------------------------------------------------------------- +/* Performs a TLS handshake on the socket */ +struct HttpConnectFd : public MethodFd +{ + std::unique_ptr<MethodFd> UnderlyingFd; + std::string Buffer; + + int Fd() APT_OVERRIDE { return UnderlyingFd->Fd(); } + + ssize_t Read(void *buf, size_t count) APT_OVERRIDE + { + if (!Buffer.empty()) + { + auto read = count < Buffer.size() ? count : Buffer.size(); + + memcpy(buf, Buffer.data(), read); + Buffer.erase(Buffer.begin(), Buffer.begin() + read); + return read; + } + + return UnderlyingFd->Read(buf, count); + } + ssize_t Write(void *buf, size_t count) APT_OVERRIDE + { + return UnderlyingFd->Write(buf, count); + } + + int Close() APT_OVERRIDE + { + return UnderlyingFd->Close(); + } + + bool HasPending() APT_OVERRIDE + { + return !Buffer.empty(); + } +}; + +bool UnwrapHTTPConnect(std::string Host, int Port, URI Proxy, std::unique_ptr<MethodFd> &Fd, + unsigned long Timeout, aptMethod *Owner) +{ + Owner->Status(_("Connecting to %s (%s)"), "HTTP proxy", URI::SiteOnly(Proxy).c_str()); + // The HTTP server expects a hostname with a trailing :port + std::stringstream Req; + std::string ProperHost; + + if (Host.find(':') != std::string::npos) + ProperHost = '[' + Proxy.Host + ']'; + else + ProperHost = Proxy.Host; + + // Build the connect + Req << "CONNECT " << Host << ":" << std::to_string(Port) << " HTTP/1.1\r\n"; + if (Proxy.Port != 0) + Req << "Host: " << ProperHost << ":" << std::to_string(Proxy.Port) << "\r\n"; + else + Req << "Host: " << ProperHost << "\r\n"; + ; + + maybe_add_auth(Proxy, _config->FindFile("Dir::Etc::netrc")); + if (Proxy.User.empty() == false || Proxy.Password.empty() == false) + Req << "Proxy-Authorization: Basic " + << Base64Encode(Proxy.User + ":" + Proxy.Password) << "\r\n"; + + Req << "User-Agent: " << Owner->ConfigFind("User-Agent", "Debian APT-HTTP/1.3 (" PACKAGE_VERSION ")") << "\r\n"; + + Req << "\r\n"; + + CircleBuf In(dynamic_cast<HttpMethod *>(Owner), 4096); + CircleBuf Out(dynamic_cast<HttpMethod *>(Owner), 4096); + std::string Headers; + + if (Owner->DebugEnabled() == true) + cerr << Req.str() << endl; + Out.Read(Req.str()); + + // Writing from proxy + while (Out.WriteSpace() > 0) + { + if (WaitFd(Fd->Fd(), true, Timeout) == false) + return _error->Errno("select", "Writing to proxy failed"); + if (Out.Write(Fd) == false) + return _error->Errno("write", "Writing to proxy failed"); + } + + while (In.ReadSpace() > 0) + { + if (WaitFd(Fd->Fd(), false, Timeout) == false) + return _error->Errno("select", "Reading from proxy failed"); + if (In.Read(Fd) == false) + return _error->Errno("read", "Reading from proxy failed"); + + if (In.WriteTillEl(Headers)) + break; + } + + if (Owner->DebugEnabled() == true) + cerr << Headers << endl; + + if (!(APT::String::Startswith(Headers, "HTTP/1.0 200") || APT::String::Startswith(Headers, "HTTP/1.1 200"))) + return _error->Error("Invalid response from proxy: %s", Headers.c_str()); + + if (In.WriteSpace() > 0) + { + // Maybe there is actual data already read, if so we need to buffer it + std::unique_ptr<HttpConnectFd> NewFd(new HttpConnectFd()); + In.Write(NewFd->Buffer); + NewFd->UnderlyingFd = std::move(Fd); + Fd = std::move(NewFd); + } + + return true; +} + /*}}}*/ + // HttpServerState::HttpServerState - Constructor /*{{{*/ HttpServerState::HttpServerState(URI Srv,HttpMethod *Owner) : ServerState(Srv, Owner), In(Owner, 64*1024), Out(Owner, 4*1024) { TimeOut = Owner->ConfigFindI("Timeout", TimeOut); + ServerFd = MethodFd::FromFd(-1); Reset(); } /*}}}*/ // HttpServerState::Open - Open a connection to the server /*{{{*/ // --------------------------------------------------------------------- /* This opens a connection to the server. */ -static bool TalkToSocksProxy(int const ServerFd, std::string const &Proxy, - char const * const type, bool const ReadWrite, uint8_t * const ToFrom, - unsigned int const Size, unsigned int const Timeout) -{ - if (WaitFd(ServerFd, ReadWrite, Timeout) == false) - return _error->Error("Waiting for the SOCKS proxy %s to %s timed out", URI::SiteOnly(Proxy).c_str(), type); - if (ReadWrite == false) - { - if (FileFd::Read(ServerFd, ToFrom, Size) == false) - return _error->Error("Reading the %s from SOCKS proxy %s failed", type, URI::SiteOnly(Proxy).c_str()); - } - else - { - if (FileFd::Write(ServerFd, ToFrom, Size) == false) - return _error->Error("Writing the %s to SOCKS proxy %s failed", type, URI::SiteOnly(Proxy).c_str()); - } - return true; -} bool HttpServerState::Open() { // Use the already open connection if possible. - if (ServerFd != -1) + if (ServerFd->Fd() != -1) return true; Close(); @@ -360,165 +460,17 @@ bool HttpServerState::Open() if (Proxy.empty() == false) Owner->AddProxyAuth(Proxy, ServerName); + bool tls = (ServerName.Access == "https" || APT::String::Endswith(ServerName.Access, "+https")); + auto const DefaultService = tls ? "https" : "http"; + auto const DefaultPort = tls ? 443 : 80; if (Proxy.Access == "socks5h") { if (Connect(Proxy.Host, Proxy.Port, "socks", 1080, ServerFd, TimeOut, Owner) == false) return false; - /* We implement a very basic SOCKS5 client here complying mostly to RFC1928 expect - * for not offering GSSAPI auth which is a must (we only do no or user/pass auth). - * We also expect the SOCKS5 server to do hostname lookup (aka socks5h) */ - std::string const ProxyInfo = URI::SiteOnly(Proxy); - Owner->Status(_("Connecting to %s (%s)"),"SOCKS5h proxy",ProxyInfo.c_str()); - auto const Timeout = Owner->ConfigFindI("TimeOut", 120); - #define APT_WriteOrFail(TYPE, DATA, LENGTH) if (TalkToSocksProxy(ServerFd, ProxyInfo, TYPE, true, DATA, LENGTH, Timeout) == false) return false - #define APT_ReadOrFail(TYPE, DATA, LENGTH) if (TalkToSocksProxy(ServerFd, ProxyInfo, TYPE, false, DATA, LENGTH, Timeout) == false) return false - if (ServerName.Host.length() > 255) - return _error->Error("Can't use SOCKS5h as hostname %s is too long!", ServerName.Host.c_str()); - if (Proxy.User.length() > 255 || Proxy.Password.length() > 255) - return _error->Error("Can't use user&pass auth as they are too long (%lu and %lu) for the SOCKS5!", Proxy.User.length(), Proxy.Password.length()); - if (Proxy.User.empty()) - { - uint8_t greeting[] = { 0x05, 0x01, 0x00 }; - APT_WriteOrFail("greet-1", greeting, sizeof(greeting)); - } - else - { - uint8_t greeting[] = { 0x05, 0x02, 0x00, 0x02 }; - APT_WriteOrFail("greet-2", greeting, sizeof(greeting)); - } - uint8_t greeting[2]; - APT_ReadOrFail("greet back", greeting, sizeof(greeting)); - if (greeting[0] != 0x05) - return _error->Error("SOCKS proxy %s greets back with wrong version: %d", ProxyInfo.c_str(), greeting[0]); - if (greeting[1] == 0x00) - ; // no auth has no method-dependent sub-negotiations - else if (greeting[1] == 0x02) - { - if (Proxy.User.empty()) - return _error->Error("SOCKS proxy %s negotiated user&pass auth, but we had not offered it!", ProxyInfo.c_str()); - // user&pass auth sub-negotiations are defined by RFC1929 - std::vector<uint8_t> auth = {{ 0x01, static_cast<uint8_t>(Proxy.User.length()) }}; - std::copy(Proxy.User.begin(), Proxy.User.end(), std::back_inserter(auth)); - auth.push_back(static_cast<uint8_t>(Proxy.Password.length())); - std::copy(Proxy.Password.begin(), Proxy.Password.end(), std::back_inserter(auth)); - APT_WriteOrFail("user&pass auth", auth.data(), auth.size()); - uint8_t authstatus[2]; - APT_ReadOrFail("auth report", authstatus, sizeof(authstatus)); - if (authstatus[0] != 0x01) - return _error->Error("SOCKS proxy %s auth status response with wrong version: %d", ProxyInfo.c_str(), authstatus[0]); - if (authstatus[1] != 0x00) - return _error->Error("SOCKS proxy %s reported authorization failure: username or password incorrect? (%d)", ProxyInfo.c_str(), authstatus[1]); - } - else - return _error->Error("SOCKS proxy %s greets back having not found a common authorization method: %d", ProxyInfo.c_str(), greeting[1]); - union { uint16_t * i; uint8_t * b; } portu; - uint16_t port = htons(static_cast<uint16_t>(ServerName.Port == 0 ? 80 : ServerName.Port)); - portu.i = &port; - std::vector<uint8_t> request = {{ 0x05, 0x01, 0x00, 0x03, static_cast<uint8_t>(ServerName.Host.length()) }}; - std::copy(ServerName.Host.begin(), ServerName.Host.end(), std::back_inserter(request)); - request.push_back(portu.b[0]); - request.push_back(portu.b[1]); - APT_WriteOrFail("request", request.data(), request.size()); - uint8_t response[4]; - APT_ReadOrFail("first part of response", response, sizeof(response)); - if (response[0] != 0x05) - return _error->Error("SOCKS proxy %s response with wrong version: %d", ProxyInfo.c_str(), response[0]); - if (response[2] != 0x00) - return _error->Error("SOCKS proxy %s has unexpected non-zero reserved field value: %d", ProxyInfo.c_str(), response[2]); - std::string bindaddr; - if (response[3] == 0x01) // IPv4 address - { - uint8_t ip4port[6]; - APT_ReadOrFail("IPv4+Port of response", ip4port, sizeof(ip4port)); - portu.b[0] = ip4port[4]; - portu.b[1] = ip4port[5]; - port = ntohs(*portu.i); - strprintf(bindaddr, "%d.%d.%d.%d:%d", ip4port[0], ip4port[1], ip4port[2], ip4port[3], port); - } - else if (response[3] == 0x03) // hostname - { - uint8_t namelength; - APT_ReadOrFail("hostname length of response", &namelength, 1); - uint8_t hostname[namelength + 2]; - APT_ReadOrFail("hostname of response", hostname, sizeof(hostname)); - portu.b[0] = hostname[namelength]; - portu.b[1] = hostname[namelength + 1]; - port = ntohs(*portu.i); - hostname[namelength] = '\0'; - strprintf(bindaddr, "%s:%d", hostname, port); - } - else if (response[3] == 0x04) // IPv6 address - { - uint8_t ip6port[18]; - APT_ReadOrFail("IPv6+port of response", ip6port, sizeof(ip6port)); - portu.b[0] = ip6port[16]; - portu.b[1] = ip6port[17]; - port = ntohs(*portu.i); - strprintf(bindaddr, "[%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X]:%d", - ip6port[0], ip6port[1], ip6port[2], ip6port[3], ip6port[4], ip6port[5], ip6port[6], ip6port[7], - ip6port[8], ip6port[9], ip6port[10], ip6port[11], ip6port[12], ip6port[13], ip6port[14], ip6port[15], - port); - } - else - return _error->Error("SOCKS proxy %s destination address is of unknown type: %d", - ProxyInfo.c_str(), response[3]); - if (response[1] != 0x00) - { - char const * errstr = nullptr; - auto errcode = response[1]; - // Tor error reporting can be a bit arcane, lets try to detect & fix it up - if (bindaddr == "0.0.0.0:0") - { - auto const lastdot = ServerName.Host.rfind('.'); - if (lastdot == std::string::npos || ServerName.Host.substr(lastdot) != ".onion") - ; - else if (errcode == 0x01) - { - auto const prevdot = ServerName.Host.rfind('.', lastdot - 1); - if (lastdot == 16 && prevdot == std::string::npos) - ; // valid .onion address - else if (prevdot != std::string::npos && (lastdot - prevdot) == 17) - ; // valid .onion address with subdomain(s) - else - { - errstr = "Invalid hostname: onion service name must be 16 characters long"; - Owner->SetFailReason("SOCKS"); - } - } - // in all likelihood the service is either down or the address has - // a typo and so "Host unreachable" is the better understood error - // compared to the technically correct "TLL expired". - else if (errcode == 0x06) - errcode = 0x04; - } - if (errstr == nullptr) - { - switch (errcode) - { - case 0x01: errstr = "general SOCKS server failure"; Owner->SetFailReason("SOCKS"); break; - case 0x02: errstr = "connection not allowed by ruleset"; Owner->SetFailReason("SOCKS"); break; - case 0x03: errstr = "Network unreachable"; Owner->SetFailReason("ConnectionTimedOut"); break; - case 0x04: errstr = "Host unreachable"; Owner->SetFailReason("ConnectionTimedOut"); break; - case 0x05: errstr = "Connection refused"; Owner->SetFailReason("ConnectionRefused"); break; - case 0x06: errstr = "TTL expired"; Owner->SetFailReason("Timeout"); break; - case 0x07: errstr = "Command not supported"; Owner->SetFailReason("SOCKS"); break; - case 0x08: errstr = "Address type not supported"; Owner->SetFailReason("SOCKS"); break; - default: errstr = "Unknown error"; Owner->SetFailReason("SOCKS"); break; - } - } - return _error->Error("SOCKS proxy %s could not connect to %s (%s) due to: %s (%d)", - ProxyInfo.c_str(), ServerName.Host.c_str(), bindaddr.c_str(), errstr, response[1]); - } - else if (Owner->DebugEnabled()) - ioprintf(std::clog, "http: SOCKS proxy %s connection established to %s (%s)\n", - ProxyInfo.c_str(), ServerName.Host.c_str(), bindaddr.c_str()); - - if (WaitFd(ServerFd, true, Timeout) == false) - return _error->Error("SOCKS proxy %s reported connection to %s (%s), but timed out", - ProxyInfo.c_str(), ServerName.Host.c_str(), bindaddr.c_str()); - #undef APT_ReadOrFail - #undef APT_WriteOrFail + if (UnwrapSocks(ServerName.Host, ServerName.Port == 0 ? DefaultPort : ServerName.Port, + Proxy, ServerFd, Owner->ConfigFindI("TimeOut", 120), Owner) == false) + return false; } else { @@ -531,16 +483,28 @@ bool HttpServerState::Open() Port = ServerName.Port; Host = ServerName.Host; } - else if (Proxy.Access != "http") + else if (Proxy.Access != "http" && Proxy.Access != "https") return _error->Error("Unsupported proxy configured: %s", URI::SiteOnly(Proxy).c_str()); else { if (Proxy.Port != 0) Port = Proxy.Port; Host = Proxy.Host; + + if (Proxy.Access == "https" && Port == 0) + Port = 443; } - return Connect(Host,Port,"http",80,ServerFd,TimeOut,Owner); + if (!Connect(Host, Port, DefaultService, DefaultPort, ServerFd, TimeOut, Owner)) + return false; + if (Host == Proxy.Host && Proxy.Access == "https" && UnwrapTLS(Proxy.Host, ServerFd, TimeOut, Owner) == false) + return false; + if (Host == Proxy.Host && tls && UnwrapHTTPConnect(ServerName.Host, ServerName.Port == 0 ? DefaultPort : ServerName.Port, Proxy, ServerFd, Owner->ConfigFindI("TimeOut", 120), Owner) == false) + return false; } + + if (tls && UnwrapTLS(ServerName.Host, ServerFd, TimeOut, Owner) == false) + return false; + return true; } /*}}}*/ @@ -549,8 +513,7 @@ bool HttpServerState::Open() /* */ bool HttpServerState::Close() { - close(ServerFd); - ServerFd = -1; + ServerFd->Close(); return true; } /*}}}*/ @@ -672,7 +635,7 @@ bool HttpServerState::WriteResponse(const std::string &Data) /*{{{*/ /*}}}*/ APT_PURE bool HttpServerState::IsOpen() /*{{{*/ { - return (ServerFd != -1); + return (ServerFd->Fd() != -1); } /*}}}*/ bool HttpServerState::InitHashes(HashStringList const &ExpectedHashes) /*{{{*/ @@ -685,7 +648,7 @@ bool HttpServerState::InitHashes(HashStringList const &ExpectedHashes) /*{{{*/ void HttpServerState::Reset() /*{{{*/ { ServerState::Reset(); - ServerFd = -1; + ServerFd->Close(); } /*}}}*/ @@ -710,7 +673,7 @@ bool HttpServerState::Die(RequestState &Req) SetNonBlock(Req.File.Fd(),false); while (In.WriteSpace() == true) { - if (In.Write(Req.File.Fd()) == false) + if (In.Write(MethodFd::FromFd(Req.File.Fd())) == false) return _error->Errno("write",_("Error writing to the file")); // Done @@ -762,7 +725,7 @@ bool HttpServerState::Flush(FileFd * const File) while (In.WriteSpace() == true) { - if (In.Write(File->Fd()) == false) + if (In.Write(MethodFd::FromFd(File->Fd())) == false) return _error->Errno("write",_("Error writing to file")); if (In.IsLimit() == true) return true; @@ -781,38 +744,45 @@ bool HttpServerState::Flush(FileFd * const File) bool HttpServerState::Go(bool ToFile, RequestState &Req) { // Server has closed the connection - if (ServerFd == -1 && (In.WriteSpace() == false || - ToFile == false)) + if (ServerFd->Fd() == -1 && (In.WriteSpace() == false || + ToFile == false)) return false; - + + // Handle server IO + if (ServerFd->HasPending() && In.ReadSpace() == true) + { + errno = 0; + if (In.Read(ServerFd) == false) + return Die(Req); + } + fd_set rfds,wfds; FD_ZERO(&rfds); FD_ZERO(&wfds); /* Add the server. We only send more requests if the connection will be persisting */ - if (Out.WriteSpace() == true && ServerFd != -1 - && Persistent == true) - FD_SET(ServerFd,&wfds); - if (In.ReadSpace() == true && ServerFd != -1) - FD_SET(ServerFd,&rfds); - + if (Out.WriteSpace() == true && ServerFd->Fd() != -1 && Persistent == true) + FD_SET(ServerFd->Fd(), &wfds); + if (In.ReadSpace() == true && ServerFd->Fd() != -1) + FD_SET(ServerFd->Fd(), &rfds); + // Add the file - int FileFD = -1; + auto FileFD = MethodFd::FromFd(-1); if (Req.File.IsOpen()) - FileFD = Req.File.Fd(); - - if (In.WriteSpace() == true && ToFile == true && FileFD != -1) - FD_SET(FileFD,&wfds); + FileFD = MethodFd::FromFd(Req.File.Fd()); + + if (In.WriteSpace() == true && ToFile == true && FileFD->Fd() != -1) + FD_SET(FileFD->Fd(), &wfds); // Add stdin if (Owner->ConfigFindB("DependOnSTDIN", true) == true) FD_SET(STDIN_FILENO,&rfds); // Figure out the max fd - int MaxFd = FileFD; - if (MaxFd < ServerFd) - MaxFd = ServerFd; + int MaxFd = FileFD->Fd(); + if (MaxFd < ServerFd->Fd()) + MaxFd = ServerFd->Fd(); // Select struct timeval tv; @@ -833,14 +803,14 @@ bool HttpServerState::Go(bool ToFile, RequestState &Req) } // Handle server IO - if (ServerFd != -1 && FD_ISSET(ServerFd,&rfds)) + if (ServerFd->Fd() != -1 && FD_ISSET(ServerFd->Fd(), &rfds)) { errno = 0; if (In.Read(ServerFd) == false) return Die(Req); } - - if (ServerFd != -1 && FD_ISSET(ServerFd,&wfds)) + + if (ServerFd->Fd() != -1 && FD_ISSET(ServerFd->Fd(), &wfds)) { errno = 0; if (Out.Write(ServerFd) == false) @@ -848,7 +818,7 @@ bool HttpServerState::Go(bool ToFile, RequestState &Req) } // Send data to the file - if (FileFD != -1 && FD_ISSET(FileFD,&wfds)) + if (FileFD->Fd() != -1 && FD_ISSET(FileFD->Fd(), &wfds)) { if (In.Write(FileFD) == false) return _error->Errno("write",_("Error writing to output file")); @@ -897,7 +867,7 @@ void HttpMethod::SendReq(FetchItem *Itm) but while its a must for all servers to accept absolute URIs, it is assumed clients will sent an absolute path for non-proxies */ std::string requesturi; - if (Server->Proxy.Access != "http" || Server->Proxy.empty() == true || Server->Proxy.Host.empty()) + if ((Server->Proxy.Access != "http" && Server->Proxy.Access != "https") || APT::String::Endswith(Uri.Access, "https") || Server->Proxy.empty() == true || Server->Proxy.Host.empty()) requesturi = Uri.Path; else requesturi = Uri; @@ -946,8 +916,8 @@ void HttpMethod::SendReq(FetchItem *Itm) else if (Itm->LastModified != 0) Req << "If-Modified-Since: " << TimeRFC1123(Itm->LastModified, false).c_str() << "\r\n"; - if (Server->Proxy.Access == "http" && - (Server->Proxy.User.empty() == false || Server->Proxy.Password.empty() == false)) + if ((Server->Proxy.Access == "http" || Server->Proxy.Access == "https") && + (Server->Proxy.User.empty() == false || Server->Proxy.Password.empty() == false)) Req << "Proxy-Authorization: Basic " << Base64Encode(Server->Proxy.User + ":" + Server->Proxy.Password) << "\r\n"; @@ -1009,6 +979,11 @@ HttpMethod::HttpMethod(std::string &&pProg) : BaseHttpMethod(pProg.c_str(), "1.2 addName = "http"; auto const plus = Binary.find('+'); if (plus != std::string::npos) + { + auto name2 = Binary.substr(plus + 1); + if (std::find(methodNames.begin(), methodNames.end(), name2) == methodNames.end()) + addName = std::move(name2); addName = Binary.substr(0, plus); + } } /*}}}*/ diff --git a/methods/http.h b/methods/http.h index c79a6454e..7a763675c 100644 --- a/methods/http.h +++ b/methods/http.h @@ -13,11 +13,13 @@ #include <apt-pkg/strutl.h> +#include <iostream> +#include <memory> #include <string> #include <sys/time.h> -#include <iostream> #include "basehttp.h" +#include "connect.h" using std::cout; using std::endl; @@ -66,11 +68,12 @@ class CircleBuf unsigned long long TotalWriten; // Read data in - bool Read(int Fd); + bool Read(std::unique_ptr<MethodFd> const &Fd); bool Read(std::string const &Data); // Write data out - bool Write(int Fd); + bool Write(std::unique_ptr<MethodFd> const &Fd); + bool Write(std::string &Data); bool WriteTillEl(std::string &Data,bool Single = false); // Control the write limit @@ -90,12 +93,14 @@ class CircleBuf ~CircleBuf(); }; +bool UnwrapHTTPConnect(std::string To, int Port, URI Proxy, std::unique_ptr<MethodFd> &Fd, unsigned long Timeout, aptMethod *Owner); + struct HttpServerState: public ServerState { // This is the connection itself. Output is data FROM the server CircleBuf In; CircleBuf Out; - int ServerFd; + std::unique_ptr<MethodFd> ServerFd; protected: virtual bool ReadHeaderLines(std::string &Data) APT_OVERRIDE; diff --git a/methods/http_main.cc b/methods/http_main.cc index 1e56044b7..792b5e22f 100644 --- a/methods/http_main.cc +++ b/methods/http_main.cc @@ -1,6 +1,6 @@ #include <config.h> -#include <apt-pkg/fileutl.h> #include <apt-pkg/error.h> +#include <apt-pkg/fileutl.h> #include <signal.h> #include "http.h" @@ -11,7 +11,7 @@ int main(int, const char *argv[]) // closes the connection (this is dealt with via ServerDie()) signal(SIGPIPE, SIG_IGN); std::string Binary = flNotDir(argv[0]); - if (Binary.find('+') == std::string::npos && Binary != "http") + if (Binary.find('+') == std::string::npos && Binary != "https" && Binary != "http") Binary.append("+http"); return HttpMethod(std::move(Binary)).Loop(); } diff --git a/methods/mirror.cc b/methods/mirror.cc index 71faaf591..b0da5c530 100644 --- a/methods/mirror.cc +++ b/methods/mirror.cc @@ -10,32 +10,35 @@ // Include Files /*{{{*/ #include <config.h> -#include <apt-pkg/aptconfiguration.h> -#include <apt-pkg/fileutl.h> #include <apt-pkg/acquire-item.h> #include <apt-pkg/acquire.h> -#include <apt-pkg/error.h> -#include <apt-pkg/sourcelist.h> +#include <apt-pkg/aptconfiguration.h> #include <apt-pkg/configuration.h> +#include <apt-pkg/error.h> +#include <apt-pkg/fileutl.h> #include <apt-pkg/metaindex.h> +#include <apt-pkg/sourcelist.h> #include <apt-pkg/strutl.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> #include <algorithm> -#include <iostream> #include <fstream> +#include <iostream> + +#include <dirent.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> #include <sys/stat.h> +#include <sys/types.h> #include <sys/utsname.h> -#include <dirent.h> +#include <unistd.h> using namespace std; -#include<sstream> +#include <sstream> -#include "mirror.h" #include "http.h" +#include "mirror.h" #include <apti18n.h> /*}}}*/ @@ -89,16 +92,12 @@ bool MirrorMethod::Clean(string Dir) pkgSourceList list; list.ReadMainList(); - DIR *D = opendir(Dir.c_str()); - if (D == 0) - return _error->Errno("opendir",_("Unable to read %s"),Dir.c_str()); - - string StartDir = SafeGetCWD(); - if (chdir(Dir.c_str()) != 0) - { - closedir(D); - return _error->Errno("chdir",_("Unable to change to %s"),Dir.c_str()); - } + int const dirfd = open(Dir.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC); + if (dirfd == -1) + return _error->Errno("open",_("Unable to read %s"), Dir.c_str()); + DIR * const D = fdopendir(dirfd); + if (D == nullptr) + return _error->Errno("fdopendir",_("Unable to read %s"),Dir.c_str()); for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D)) { @@ -122,12 +121,9 @@ bool MirrorMethod::Clean(string Dir) } // nothing found, nuke it if (I == list.end()) - RemoveFile("mirror", Dir->d_name); + RemoveFileAt("mirror", dirfd, Dir->d_name); } - closedir(D); - if (chdir(StartDir.c_str()) != 0) - return _error->Errno("chdir",_("Unable to change to %s"),StartDir.c_str()); return true; } diff --git a/methods/rfc2553emu.cc b/methods/rfc2553emu.cc index 372882769..13f79391a 100644 --- a/methods/rfc2553emu.cc +++ b/methods/rfc2553emu.cc @@ -16,12 +16,12 @@ /*}}}*/ #include <config.h> -#include <stdlib.h> +#include "rfc2553emu.h" #include <arpa/inet.h> #include <netinet/in.h> -#include <string.h> #include <stdio.h> -#include "rfc2553emu.h" +#include <stdlib.h> +#include <string.h> #ifndef HAVE_GETADDRINFO // getaddrinfo - Resolve a hostname /*{{{*/ diff --git a/methods/rfc2553emu.h b/methods/rfc2553emu.h index ad7ddf48a..462bfe26f 100644 --- a/methods/rfc2553emu.h +++ b/methods/rfc2553emu.h @@ -23,8 +23,8 @@ #define RFC2553EMU_H #include <netdb.h> -#include <sys/types.h> #include <sys/socket.h> +#include <sys/types.h> // Autosense getaddrinfo #if defined(AI_PASSIVE) && defined(EAI_NONAME) diff --git a/methods/rred.cc b/methods/rred.cc index 2e5008d46..3a3b20286 100644 --- a/methods/rred.cc +++ b/methods/rred.cc @@ -7,19 +7,19 @@ #include <config.h> -#include <apt-pkg/init.h> -#include <apt-pkg/fileutl.h> +#include "aptmethod.h" +#include <apt-pkg/configuration.h> #include <apt-pkg/error.h> -#include <apt-pkg/strutl.h> +#include <apt-pkg/fileutl.h> #include <apt-pkg/hashes.h> -#include <apt-pkg/configuration.h> -#include "aptmethod.h" +#include <apt-pkg/init.h> +#include <apt-pkg/strutl.h> -#include <stddef.h> #include <iostream> -#include <string> #include <list> +#include <string> #include <vector> +#include <stddef.h> #include <assert.h> #include <errno.h> diff --git a/methods/rsh.cc b/methods/rsh.cc index 7b8af6f9b..69a51a13d 100644 --- a/methods/rsh.cc +++ b/methods/rsh.cc @@ -13,22 +13,22 @@ // Include Files /*{{{*/ #include <config.h> +#include <apt-pkg/configuration.h> #include <apt-pkg/error.h> #include <apt-pkg/fileutl.h> #include <apt-pkg/hashes.h> -#include <apt-pkg/configuration.h> #include <apt-pkg/strutl.h> +#include "rsh.h" +#include <errno.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> #include <unistd.h> -#include <signal.h> -#include <stdio.h> -#include <errno.h> -#include <stdarg.h> -#include "rsh.h" #include <apti18n.h> /*}}}*/ diff --git a/methods/store.cc b/methods/store.cc index 1faaa4fb4..d54a14397 100644 --- a/methods/store.cc +++ b/methods/store.cc @@ -13,19 +13,19 @@ // Include Files /*{{{*/ #include <config.h> +#include "aptmethod.h" +#include <apt-pkg/aptconfiguration.h> #include <apt-pkg/configuration.h> #include <apt-pkg/error.h> #include <apt-pkg/fileutl.h> #include <apt-pkg/hashes.h> #include <apt-pkg/strutl.h> -#include <apt-pkg/aptconfiguration.h> -#include "aptmethod.h" +#include <string> +#include <vector> #include <string.h> #include <sys/stat.h> #include <sys/time.h> -#include <string> -#include <vector> #include <apti18n.h> /*}}}*/ |