[devel] [PATCH for apt 24/38] Improve ipv6 address handling

Aleksei Nikiforov darktemplar на altlinux.org
Вт Дек 10 18:23:29 MSK 2019


Introduce new class URIAddress for parsing, storing,
and converting back to string various address types,
including ipv4-addresses, ipv6-addresses and hostnames.
In addition to hostname, address may contain interface name and port number.
---
 apt/apt-pkg/acquire.cc          |   2 +-
 apt/apt-pkg/contrib/strutl.cc   | 282 ++++++++++++++++++++++++--------
 apt/apt-pkg/contrib/strutl.h    |  35 +++-
 apt/apt-pkg/rpm/rpmindexfile.cc |   2 +-
 apt/methods/cdrom.cc            |   8 +-
 apt/methods/connect.cc          |  34 ++--
 apt/methods/connect.h           |   2 +-
 apt/methods/file.cc             |   2 +-
 apt/methods/ftp.cc              |  40 ++---
 apt/methods/ftp.h               |   2 +-
 apt/methods/gpg.cc              |   2 +-
 apt/methods/gzip.cc             |   2 +-
 apt/methods/http.cc             |  49 ++----
 apt/methods/http.h              |   2 +-
 apt/methods/rsh.cc              |   4 +-
 apt/methods/rsh.h               |   2 +-
 apt/methods/rsync.cc            |  10 +-
 apt/test/uri.cc                 |  14 +-
 18 files changed, 319 insertions(+), 175 deletions(-)

diff --git a/apt/apt-pkg/acquire.cc b/apt/apt-pkg/acquire.cc
index e1e6d7c..79784af 100644
--- a/apt/apt-pkg/acquire.cc
+++ b/apt/apt-pkg/acquire.cc
@@ -236,7 +236,7 @@ string pkgAcquire::QueueName(const string &Uri,MethodConfig const *&Config)
    if (Config->SingleInstance == true || QueueMode == QueueAccess)
        return U.Access;
 
-   return U.Access + ':' + U.Host;
+   return U.Access + ':' + U.Address.to_hostname();
 }
 									/*}}}*/
 // Acquire::GetConfig - Fetch the configuration information		/*{{{*/
diff --git a/apt/apt-pkg/contrib/strutl.cc b/apt/apt-pkg/contrib/strutl.cc
index 4d5025a..52d1995 100644
--- a/apt/apt-pkg/contrib/strutl.cc
+++ b/apt/apt-pkg/contrib/strutl.cc
@@ -35,6 +35,9 @@
 #include <errno.h>
 #include <stdarg.h>
 
+#include <algorithm>
+#include <utility>
+
 using namespace std;
 									/*}}}*/
 
@@ -1058,27 +1061,211 @@ bool CheckDomainList(const string &Host, const string &List)
 }
 									/*}}}*/
 
+URIAddress::URIAddress()
+   : is_ipv6addr(false)
+{
+}
+
+URIAddress::URIAddress(const std::string &host_uri)
+   : is_ipv6addr(false)
+{
+   from_string(host_uri);
+}
+
+URIAddress::URIAddress(const URIAddress &other)
+   : hostname(other.hostname)
+   , interface(other.interface)
+   , port(other.port)
+   , is_ipv6addr(other.is_ipv6addr)
+{
+}
+
+URIAddress::URIAddress(URIAddress &&other)
+   : hostname(std::move(other.hostname))
+   , interface(std::move(other.interface))
+   , port(std::move(other.port))
+   , is_ipv6addr(std::move(other.is_ipv6addr))
+{
+}
+
+URIAddress& URIAddress::operator=(const std::string &host_uri)
+{
+   from_string(host_uri);
+
+   return *this;
+}
+
+URIAddress& URIAddress::operator=(const URIAddress &other)
+{
+   if (&other != this)
+   {
+      this->hostname    = other.hostname;
+      this->interface   = other.interface;
+      this->port        = other.port;
+      this->is_ipv6addr = other.is_ipv6addr;
+   }
+
+   return *this;
+}
+
+URIAddress& URIAddress::operator=(URIAddress &&other)
+{
+   if (&other != this)
+   {
+      this->hostname    = std::move(other.hostname);
+      this->interface   = std::move(other.interface);
+      this->port        = std::move(other.port);
+      this->is_ipv6addr = std::move(other.is_ipv6addr);
+   }
+
+   return *this;
+}
+
+bool URIAddress::operator==(const std::string &host_uri) const
+{
+   URIAddress other(host_uri);
+
+   return (*this == other);
+}
+
+bool URIAddress::operator==(const URIAddress &other) const
+{
+   return (this->hostname == other.hostname)
+         && (this->interface == other.interface)
+         && (this->port == other.port)
+         && (this->is_ipv6addr == other.is_ipv6addr);
+}
+
+std::string URIAddress::to_string() const
+{
+   std::string result = to_hostname();
+
+   if (result.empty())
+   {
+      return result;
+   }
+
+   if (port)
+   {
+      result += ':';
+      result += std::to_string(*port);
+   }
+
+   return result;
+}
+
+std::string URIAddress::to_hostname() const
+{
+   std::string result = hostname_and_interface();
+
+   if (result.empty())
+   {
+      return std::string();
+   }
+
+   if (!is_ipv6addr)
+   {
+      return result;
+   }
+
+   return std::string("[") + result + std::string("]");
+}
+
+std::string URIAddress::hostname_and_interface() const
+{
+   if (hostname.empty())
+   {
+      return std::string();
+   }
+
+   if (interface.empty())
+   {
+      return hostname;
+   }
+
+   return hostname + '%' + interface;
+}
+
+void URIAddress::from_string(const std::string &host_uri)
+{
+   std::string remainder = host_uri;
+
+   // first look for port delimiter, outside of brackets
+   size_t index = remainder.size();
+   for ( ; (index > 0) && (remainder[index - 1] != ']') && (remainder[index - 1] != ':'); --index)
+   {
+   }
+
+   if ((index > 0) && (remainder[index - 1] == ':'))
+   {
+      if (index < remainder.size())
+      {
+         port = atoi(remainder.substr(index).c_str());
+      }
+      else
+      {
+         port = std::experimental::optional<uint16_t>();
+      }
+
+      remainder = remainder.substr(0, index > 0 ? index - 1 : 0);
+   }
+   else
+   {
+      port = std::experimental::optional<uint16_t>();
+   }
+
+   if ((remainder.front() == '[') && (remainder.back() == ']'))
+   {
+      is_ipv6addr = true;
+      remainder = remainder.substr(1, remainder.size() - 2);
+   }
+   else
+   {
+      is_ipv6addr = false;
+   }
+
+   size_t percent_pos = remainder.find_last_of('%');
+   if (percent_pos != std::string::npos)
+   {
+      hostname = remainder.substr(0, percent_pos);
+
+      if (percent_pos < remainder.size() - 1)
+      {
+         interface = remainder.substr(percent_pos + 1);
+      }
+      else
+      {
+         interface = std::string();
+      }
+   }
+   else
+   {
+      hostname = remainder;
+      interface = std::string();
+   }
+}
+
 // URI::CopyFrom - Copy from an object					/*{{{*/
 // ---------------------------------------------------------------------
 /* This parses the URI into all of its components */
 void URI::CopyFrom(const string &U)
 {
-   auto I = U.begin();
+   string::const_iterator I = U.begin();
 
    // Locate the first colon, this separates the scheme
-   for (; I < U.end() && *I != ':' ; I++);
-   auto FirstColon = I;
+   for (; I < U.end() && *I != ':' ; ++I);
+   string::const_iterator FirstColon = I;
 
    /* Determine if this is a host type URI with a leading double //
       and then search for the first single / */
-   auto SingleSlash = I;
+   string::const_iterator SingleSlash = I;
    if (I + 3 < U.end() && I[1] == '/' && I[2] == '/')
       SingleSlash += 3;
    
    /* Find the / indicating the end of the hostname, ignoring /'s in the
       square brackets */
    bool InBracket = false;
-   for (; SingleSlash < U.end() && (*SingleSlash != '/' || InBracket == true); SingleSlash++)
+   for (; SingleSlash < U.end() && (*SingleSlash != '/' || InBracket == true); ++SingleSlash)
    {
       if (*SingleSlash == '[')
 	 InBracket = true;
@@ -1090,9 +1277,9 @@ void URI::CopyFrom(const string &U)
       SingleSlash = U.end();
 
    // We can now write the access and path specifiers
-   Access = string(U,0,FirstColon - U.begin());
+   Access.assign(U.begin(),FirstColon);
    if (SingleSlash != U.end())
-      Path = string(U,SingleSlash - U.begin());
+      Path.assign(SingleSlash,U.end());
    if (Path.empty() == true)
       Path = "/";
 
@@ -1111,64 +1298,33 @@ void URI::CopyFrom(const string &U)
    I = FirstColon + 1;
    if (I > SingleSlash)
       I = SingleSlash;
-   for (; I < SingleSlash && *I != ':'; I++);
-   auto SecondColon = I;
-   
-   // Search for the @ after the colon
-   for (; I < SingleSlash && *I != '@'; I++);
-   auto At = I;
+
+   // Search for the @ separating user:pass from host
+   auto const RevAt = std::find(
+	 std::string::const_reverse_iterator(SingleSlash),
+	 std::string::const_reverse_iterator(I), '@');
+   string::const_iterator const At = RevAt.base() == I ? SingleSlash : std::prev(RevAt.base());
+   // and then look for the colon between user and pass
+   string::const_iterator const SecondColon = std::find(I, At, ':');
+
+   std::string Host;
    
    // Now write the host and user/pass
    if (At == SingleSlash)
    {
       if (FirstColon < SingleSlash)
-	 Host = string(U,FirstColon - U.begin(),SingleSlash - FirstColon);
+	 Host.assign(FirstColon,SingleSlash);
    }
    else
    {
-      Host = string(U,At - U.begin() + 1,SingleSlash - At - 1);
-      User = string(U,FirstColon - U.begin(),SecondColon - FirstColon);
+      Host.assign(At+1,SingleSlash);
+      // username and password must be encoded (RFC 3986)
+      User.assign(DeQuoteString(std::string(FirstColon,SecondColon)));
       if (SecondColon < At)
-	 Password = string(U,SecondColon - U.begin() + 1,At - SecondColon - 1);
+	 Password.assign(DeQuoteString(std::string(SecondColon+1,At)));
    }   
    
-   // Now we parse the RFC 2732 [] hostnames.
-   unsigned long PortEnd = 0;
-   InBracket = false;
-   for (unsigned I = 0; I != Host.length();)
-   {
-      if (Host[I] == '[')
-      {
-	 InBracket = true;
-	 Host.erase(I,1);
-	 continue;
-      }
-      
-      if (InBracket == true && Host[I] == ']')
-      {
-	 InBracket = false;
-	 Host.erase(I,1);
-	 PortEnd = I;
-	 continue;
-      }
-      I++;
-   }
-   
-   // Tsk, weird.
-   if (InBracket == true)
-   {
-      Host = string();
-      return;
-   }
-   
-   // Now we parse off a port number from the hostname
-   Port = 0;
-   string::size_type Pos = Host.rfind(':');
-   if (Pos == string::npos || Pos < PortEnd)
-      return;
-   
-   Port = atoi(string(Host,Pos+1).c_str());
-   Host = string(Host,0,Pos);
+   Address = URIAddress(Host);
 }
 									/*}}}*/
 // URI::operator string - Convert the URI to a string			/*{{{*/
@@ -1181,7 +1337,7 @@ URI::operator string()
    if (Access.empty() == false)
       Res = Access + ':';
    
-   if (Host.empty() == false)
+   if (Address.hostname.empty() == false)
    {	 
       if (Access.empty() == false)
 	 Res += "//";
@@ -1194,19 +1350,7 @@ URI::operator string()
 	 Res += "@";
       }
       
-      // Add RFC 2732 escaping characters
-      if (Access.empty() == false &&
-	  (Host.find('/') != string::npos || Host.find(':') != string::npos))
-	 Res += '[' + Host + ']';
-      else
-	 Res += Host;
-      
-      if (Port != 0)
-      {
-	 char S[30];
-	 sprintf(S,":%u",Port);
-	 Res += S;
-      }	 
+      Res += Address.to_string();
    }
    
    if (Path.empty() == false)
@@ -1229,7 +1373,7 @@ string URI::SiteOnly(const string &URI)
    U.User.clear();
    U.Password.clear();
    U.Path.clear();
-   U.Port = 0;
+   U.Address.port = std::experimental::optional<uint16_t>();
    return U;
 }
 									/*}}}*/
diff --git a/apt/apt-pkg/contrib/strutl.h b/apt/apt-pkg/contrib/strutl.h
index 8a063f3..a13b32b 100644
--- a/apt/apt-pkg/contrib/strutl.h
+++ b/apt/apt-pkg/contrib/strutl.h
@@ -26,6 +26,8 @@
 #include <time.h>
 #include <cstring>
 
+#include <experimental/optional>
+
 using std::string;
 using std::vector;
 using std::ostream;
@@ -104,6 +106,34 @@ APT_MKSTRCMP2(stringcasecmp,stringcasecmp);
 
 inline const char *DeNull(const char *s) {return (s == 0?"(null)":s);};
 
+class URIAddress
+{
+public:
+   URIAddress();
+   URIAddress(const URIAddress &other);
+   URIAddress(URIAddress &&other);
+
+   explicit URIAddress(const std::string &host_uri);
+
+   URIAddress& operator=(const std::string &host_uri);
+   URIAddress& operator=(const URIAddress &other);
+   URIAddress& operator=(URIAddress &&other);
+
+   bool operator==(const std::string &host_uri) const;
+   bool operator==(const URIAddress &other) const;
+
+   std::string to_string() const;
+   void from_string(const std::string &host_uri);
+
+   std::string to_hostname() const;
+   std::string hostname_and_interface() const;
+
+   std::string hostname;
+   std::string interface;
+   std::experimental::optional<uint16_t> port;
+   bool is_ipv6addr;
+};
+
 class URI
 {
    void CopyFrom(const string &From);
@@ -113,9 +143,8 @@ class URI
    string Access;
    string User;
    string Password;
-   string Host;
+   URIAddress Address;
    string Path;
-   unsigned int Port;
    
    operator string();
    inline void operator =(const string &From) {CopyFrom(From);};
@@ -123,7 +152,7 @@ class URI
    static string SiteOnly(const string &URI);
    
    URI(const string &Path) {CopyFrom(Path);};
-   URI() : Port(0) {};
+   URI() {};
 };
 
 struct SubstVar
diff --git a/apt/apt-pkg/rpm/rpmindexfile.cc b/apt/apt-pkg/rpm/rpmindexfile.cc
index b4523b9..ad011a7 100644
--- a/apt/apt-pkg/rpm/rpmindexfile.cc
+++ b/apt/apt-pkg/rpm/rpmindexfile.cc
@@ -367,7 +367,7 @@ bool rpmPkgListIndex::Merge(pkgCacheGenerator &Gen,OpProgress &Prog) const
 
    Prog.SubProgress(0,Info(MainType()));
    ::URI Tmp(URI);
-   if (Gen.SelectFile(PackageFile,Tmp.Host,*this) == false)
+   if (Gen.SelectFile(PackageFile,Tmp.Address.to_hostname(),*this) == false)
    {
       delete Handler;
       return _error->Error(_("Problem with SelectFile %s"),PackageFile.c_str());
diff --git a/apt/methods/cdrom.cc b/apt/methods/cdrom.cc
index 9e04c3e..cfe0869 100644
--- a/apt/methods/cdrom.cc
+++ b/apt/methods/cdrom.cc
@@ -198,7 +198,7 @@ bool CDROMMethod::Fetch(FetchItem *Itm)
    }
        
    // All non IMS queries for package files fail.
-   if (Itm->IndexFile == true || GetID(Get.Host).empty() == true)
+   if (Itm->IndexFile == true || GetID(Get.Address.to_hostname()).empty() == true)
    {
       Fail(_("Please use apt-cdrom to make this media recognized by APT."
 	   " apt-get update cannot be used to add new Media"));
@@ -206,7 +206,7 @@ bool CDROMMethod::Fetch(FetchItem *Itm)
    }
 
    // We already have a CD inserted, but it is the wrong one
-   if (CurrentID.empty() == false && Database.Find("CD::" + CurrentID) != Get.Host)
+   if (CurrentID.empty() == false && Database.Find("CD::" + CurrentID) != Get.Address.to_hostname())
    {
       Fail(_("Wrong CD"),true);
       return true;
@@ -229,7 +229,7 @@ bool CDROMMethod::Fetch(FetchItem *Itm)
 	    clog << "ID " << Version << " " << NewID << endl;
       
 	 // A hit
-	 if (Database.Find("CD::" + NewID) == Get.Host)
+	 if (Database.Find("CD::" + NewID) == Get.Address.to_hostname())
 	 {
 	    Hit = true;
 	    break;
@@ -243,7 +243,7 @@ bool CDROMMethod::Fetch(FetchItem *Itm)
       if (UnmountCdrom(CDROM) == false)
 	 return _error->Error(_("Unable to unmount media in %s, it may still be in use."),
 			      CDROM.c_str());
-      if (MediaFail(Get.Host,CDROM) == false)
+      if (MediaFail(Get.Address.to_hostname(),CDROM) == false)
       {
 	 CurrentID = "FAIL";
 	 Fail(_("Wrong media"),true);
diff --git a/apt/methods/connect.cc b/apt/methods/connect.cc
index a25d6b4..a9edd7f 100644
--- a/apt/methods/connect.cc
+++ b/apt/methods/connect.cc
@@ -42,7 +42,7 @@
 									/*}}}*/
 
 static string LastHost;
-static int LastPort = 0;
+static std::string LastPort;
 static struct addrinfo *LastHostAddr = 0;
 static struct addrinfo *LastUsed = 0;
 
@@ -153,25 +153,25 @@ static bool DoConnect(struct addrinfo *Addr,const string &Host,
 // Connect - Connect to a server					/*{{{*/
 // ---------------------------------------------------------------------
 /* Performs a connection to the server */
-bool Connect(const string &Host,int Port,const char *Service,int DefPort,std::unique_ptr<MethodFd> &Fd,
+bool Connect(const URIAddress &address,const char *Service,int DefPort,std::unique_ptr<MethodFd> &Fd,
 	     unsigned long TimeOut,pkgAcqMethod *Owner)
 {
    if (_error->PendingError() == true)
       return false;
 
    // Convert the port name/number
-   char ServStr[300];
-   if (Port != 0)
-      snprintf(ServStr,sizeof(ServStr),"%u",(unsigned) Port);
+   std::string ServStr;
+   if (address.port)
+      ServStr = std::to_string(*(address.port));
    else
-      snprintf(ServStr,sizeof(ServStr),"%s",Service);
+      ServStr = Service;
    
    /* We used a cached address record.. Yes this is against the spec but
       the way we have setup our rotating dns suggests that this is more
       sensible */
-   if (LastHost != Host || LastPort != Port)
+   if (LastHost != address.to_hostname() || LastPort != ServStr)
    {
-      Owner->Status(_("Connecting to %s"),Host.c_str());
+      Owner->Status(_("Connecting to %s"),address.to_hostname().c_str());
 
       // Free the old address structure
       if (LastHostAddr != 0)
@@ -191,31 +191,31 @@ bool Connect(const string &Host,int Port,const char *Service,int DefPort,std::un
       while (1)
       {
 	 int Res;
-	 if ((Res = getaddrinfo(Host.c_str(),ServStr,&Hints,&LastHostAddr)) != 0 ||
+	 if ((Res = getaddrinfo(address.hostname_and_interface().c_str(),ServStr.c_str(),&Hints,&LastHostAddr)) != 0 ||
 	     LastHostAddr == 0)
 	 {
 	    if (Res == EAI_NONAME || Res == EAI_SERVICE)
 	    {
 	       if (DefPort != 0)
 	       {
-		  snprintf(ServStr,sizeof(ServStr),"%u",(unsigned) DefPort);
+		  ServStr = std::to_string((unsigned) DefPort);
 		  DefPort = 0;
 		  continue;
 	       }
-	       return _error->Error(_("Could not resolve '%s'"),Host.c_str());
+	       return _error->Error(_("Could not resolve '%s'"),address.to_hostname().c_str());
 	    }
 	    
 	    if (Res == EAI_AGAIN)
 	       return _error->Error(_("Temporary failure resolving '%s'"),
-				    Host.c_str());
+				    address.to_hostname().c_str());
 	    return _error->Error(_("Something wicked happened resolving '%s:%s' (%i)"),
-				 Host.c_str(),ServStr,Res);
+				 address.to_hostname().c_str(),ServStr.c_str(),Res);
 	 }
 	 break;
       }
       
-      LastHost = Host;
-      LastPort = Port;
+      LastHost = address.to_hostname();
+      LastPort = ServStr;
    }
 
    // When we have an IP rotation stay with the last IP.
@@ -225,7 +225,7 @@ bool Connect(const string &Host,int Port,const char *Service,int DefPort,std::un
    
    while (CurHost != 0)
    {
-      if (DoConnect(CurHost,Host,TimeOut,Fd,Owner) == true)
+      if (DoConnect(CurHost,address.to_hostname(),TimeOut,Fd,Owner) == true)
       {
 	 LastUsed = CurHost;
 	 return true;
@@ -256,7 +256,7 @@ bool Connect(const string &Host,int Port,const char *Service,int DefPort,std::un
 
    if (_error->PendingError() == true)
       return false;   
-   return _error->Error(_("Unable to connect to %s %s:"),Host.c_str(),ServStr);
+   return _error->Error(_("Unable to connect to %s %s:"),address.to_hostname().c_str(),ServStr.c_str());
 }
 									/*}}}*/
 
diff --git a/apt/methods/connect.h b/apt/methods/connect.h
index e0cafba..89b4b8c 100644
--- a/apt/methods/connect.h
+++ b/apt/methods/connect.h
@@ -36,7 +36,7 @@ struct MethodFd
    virtual bool HasPending();
 };
 
-bool Connect(const string &To,int Port,const char *Service,int DefPort,
+bool Connect(const URIAddress &address,const char *Service,int DefPort,
 	     std::unique_ptr<MethodFd> &Fd,unsigned long TimeOut,pkgAcqMethod *Owner);
 void RotateDNS();
 
diff --git a/apt/methods/file.cc b/apt/methods/file.cc
index f3a13d2..d849ca6 100644
--- a/apt/methods/file.cc
+++ b/apt/methods/file.cc
@@ -42,7 +42,7 @@ bool FileMethod::Fetch(FetchItem *Itm)
    URI Get = Itm->Uri;
    string File = Get.Path;
    FetchResult Res;
-   if (Get.Host.empty() == false)
+   if (Get.Address.to_hostname().empty() == false)
       return _error->Error(_("Invalid URI, local URIS must not start with //"));
 
    // See if the file exists
diff --git a/apt/methods/ftp.cc b/apt/methods/ftp.cc
index 00a5502..e79e70d 100644
--- a/apt/methods/ftp.cc
+++ b/apt/methods/ftp.cc
@@ -119,7 +119,7 @@ bool FTPConn::Open(pkgAcqMethod *Owner)
    if (getenv("ftp_proxy") == 0)
    {
       string DefProxy = _config->Find("Acquire::ftp::Proxy");
-      string SpecificProxy = _config->Find("Acquire::ftp::Proxy::" + ServerName.Host);
+      string SpecificProxy = _config->Find("Acquire::ftp::Proxy::" + ServerName.Address.to_hostname());
       if (SpecificProxy.empty() == false)
       {
 	 if (SpecificProxy == "DIRECT")
@@ -136,30 +136,14 @@ bool FTPConn::Open(pkgAcqMethod *Owner)
    // Parse no_proxy, a , separated list of domains
    if (getenv("no_proxy") != 0)
    {
-      if (CheckDomainList(ServerName.Host,getenv("no_proxy")) == true)
+      if (CheckDomainList(ServerName.Address.to_hostname(),getenv("no_proxy")) == true)
 	 Proxy = "";
    }
-   
-   // Determine what host and port to use based on the proxy settings
-   int Port = 0;
-   string Host;   
-   if (Proxy.empty() == true)
-   {
-      if (ServerName.Port != 0)
-	 Port = ServerName.Port;
-      Host = ServerName.Host;
-   }
-   else
-   {
-      if (Proxy.Port != 0)
-	 Port = Proxy.Port;
-      Host = Proxy.Host;
-   }
 
    /* Connect to the remote server. Since FTP is connection oriented we
       want to make sure we get a new server every time we reconnect */
    RotateDNS();
-   if (Connect(Host,Port,"ftp",21,ServerFd,TimeOut,Owner) == false)
+   if (Connect(Proxy.empty() ? ServerName.Address : Proxy.Address,"ftp",21,ServerFd,TimeOut,Owner) == false)
       return false;
 
    // Login must be before getpeername otherwise dante won't work.
@@ -223,8 +207,8 @@ bool FTPConn::Login()
       }
       
       // Enter passive mode
-      if (_config->Exists("Acquire::FTP::Passive::" + ServerName.Host) == true)
-	 TryPassive = _config->FindB("Acquire::FTP::Passive::" + ServerName.Host,true);
+      if (_config->Exists("Acquire::FTP::Passive::" + ServerName.Address.to_hostname()) == true)
+	 TryPassive = _config->FindB("Acquire::FTP::Passive::" + ServerName.Address.to_hostname(),true);
       else
 	 TryPassive = _config->FindB("Acquire::FTP::Passive",true);      
    }
@@ -251,8 +235,8 @@ bool FTPConn::Login()
 	 
 	 // Substitute the variables into the command
 	 char SitePort[20];
-	 if (ServerName.Port != 0)
-	    sprintf(SitePort,"%u",ServerName.Port);
+	 if (ServerName.Address.port)
+	    sprintf(SitePort,"%u",*(ServerName.Address.port));
 	 else
 	    strcpy(SitePort,"21");
 	 string Tmp = Opts->Value;
@@ -261,7 +245,7 @@ bool FTPConn::Login()
 	 Tmp = SubstVar(Tmp,"$(SITE_USER)",User);
 	 Tmp = SubstVar(Tmp,"$(SITE_PASS)",Pass);
 	 Tmp = SubstVar(Tmp,"$(SITE_PORT)",SitePort);
-	 Tmp = SubstVar(Tmp,"$(SITE)",ServerName.Host);
+	 Tmp = SubstVar(Tmp,"$(SITE)",ServerName.Address.to_hostname());
 
 	 // Send the command
 	 if (WriteMsg(Tag,Msg,"%s",Tmp.c_str()) == false)
@@ -272,8 +256,8 @@ bool FTPConn::Login()
       
       // Enter passive mode
       TryPassive = false;
-      if (_config->Exists("Acquire::FTP::Passive::" + ServerName.Host) == true)
-	 TryPassive = _config->FindB("Acquire::FTP::Passive::" + ServerName.Host,true);
+      if (_config->Exists("Acquire::FTP::Passive::" + ServerName.Address.to_hostname()) == true)
+	 TryPassive = _config->FindB("Acquire::FTP::Passive::" + ServerName.Address.to_hostname(),true);
       else
       {
 	 if (_config->Exists("Acquire::FTP::Proxy::Passive") == true)
@@ -284,8 +268,8 @@ bool FTPConn::Login()
    }
 
    // Force the use of extended commands
-   if (_config->Exists("Acquire::FTP::ForceExtended::" + ServerName.Host) == true)
-      ForceExtended = _config->FindB("Acquire::FTP::ForceExtended::" + ServerName.Host,true);
+   if (_config->Exists("Acquire::FTP::ForceExtended::" + ServerName.Address.to_hostname()) == true)
+      ForceExtended = _config->FindB("Acquire::FTP::ForceExtended::" + ServerName.Address.to_hostname(),true);
    else
       ForceExtended = _config->FindB("Acquire::FTP::ForceExtended",false);
    
diff --git a/apt/methods/ftp.h b/apt/methods/ftp.h
index b4a30fe..5828eea 100644
--- a/apt/methods/ftp.h
+++ b/apt/methods/ftp.h
@@ -41,7 +41,7 @@ class FTPConn
    
    public:
 
-   bool Comp(URI Other) {return Other.Host == ServerName.Host && Other.Port == ServerName.Port;};
+   bool Comp(URI Other) {return Other.Address == ServerName.Address;};
    
    // Raw connection IO
    bool ReadResp(unsigned int &Ret,string &Text);
diff --git a/apt/methods/gpg.cc b/apt/methods/gpg.cc
index 2925908..7179cef 100644
--- a/apt/methods/gpg.cc
+++ b/apt/methods/gpg.cc
@@ -337,7 +337,7 @@ void removeTmpDir(const string &path, int sigCount)
 bool GPGMethod::Fetch(FetchItem *Itm)
 {
    URI Get = Itm->Uri;
-   string Path = Get.Host + Get.Path; // To account for relative paths
+   string Path = Get.Address.to_hostname() + Get.Path; // To account for relative paths
    string KeyList;
 
    FetchResult Res;
diff --git a/apt/methods/gzip.cc b/apt/methods/gzip.cc
index db61e69..9ecc368 100644
--- a/apt/methods/gzip.cc
+++ b/apt/methods/gzip.cc
@@ -45,7 +45,7 @@ class GzipMethod : public pkgAcqMethod
 bool GzipMethod::Fetch(FetchItem *Itm)
 {
    URI Get = Itm->Uri;
-   string Path = Get.Host + Get.Path; // To account for relative paths
+   string Path = Get.Address.to_hostname() + Get.Path; // To account for relative paths
    
    string GzPathOption = "Dir::bin::"+string(Prog);
 
diff --git a/apt/methods/http.cc b/apt/methods/http.cc
index c9a37de..9302495 100644
--- a/apt/methods/http.cc
+++ b/apt/methods/http.cc
@@ -293,7 +293,7 @@ bool ServerState::Open()
    if (getenv("http_proxy") == 0)
    {
       string DefProxy = _config->Find("Acquire::http::Proxy");
-      string SpecificProxy = _config->Find("Acquire::http::Proxy::" + ServerName.Host);
+      string SpecificProxy = _config->Find("Acquire::http::Proxy::" + ServerName.Address.to_hostname());
       if (SpecificProxy.empty() == false)
       {
 	 if (SpecificProxy == "DIRECT")
@@ -310,37 +310,23 @@ bool ServerState::Open()
    // Parse no_proxy, a , separated list of domains
    if (getenv("no_proxy") != 0)
    {
-      if (CheckDomainList(ServerName.Host,getenv("no_proxy")) == true)
+      if (CheckDomainList(ServerName.Address.to_hostname(),getenv("no_proxy")) == true)
 	 Proxy = "";
    }
 #endif /* !USE_TLS */
-   
-   // Determine what host and port to use based on the proxy settings
-   int Port = 0;
-   string Host;   
-#ifndef USE_TLS
-   if (Proxy.empty() == true || Proxy.Host.empty() == true)
-   {
-#endif /* !USE_TLS */
-      if (ServerName.Port != 0)
-	 Port = ServerName.Port;
-      Host = ServerName.Host;
-#ifndef USE_TLS
-   }
-   else
-   {
-      if (Proxy.Port != 0)
-	 Port = Proxy.Port;
-      Host = Proxy.Host;
-   }
-#endif /* !USE_TLS */
 
    // Connect to the remote server
-   if (Connect(Host,Port,service_name,default_port,ServerFd,TimeOut,Owner) == false)
+   if (Connect(
+#ifndef USE_TLS
+          Proxy.empty() ? ServerName.Address : Proxy.Address,
+#else /* USE_TLS */
+          ServerName.Address,
+#endif /* USE_TLS */
+          service_name,default_port,ServerFd,TimeOut,Owner) == false)
       return false;
 
 #ifdef USE_TLS
-   if (!UnwrapTLS(ServerName.Host, ServerFd, TimeOut, Owner))
+   if (!UnwrapTLS(ServerName.Address.to_hostname(), ServerFd, TimeOut, Owner))
       return false;
 #endif /* USE_TLS */
 
@@ -639,12 +625,7 @@ void HttpMethod::SendReq(FetchItem *Itm,CircleBuf &Out)
 
    // The HTTP server expects a hostname with a trailing :port
    char Buf[1000];
-   string ProperHost = Uri.Host;
-   if (Uri.Port != 0)
-   {
-      sprintf(Buf,":%u",Uri.Port);
-      ProperHost += Buf;
-   }   
+   string ProperHost = Uri.Address.to_string();
       
    // Just in case.
    if (Itm->Uri.length() >= sizeof(Buf))
@@ -944,7 +925,7 @@ int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv)
       {
 	 for (CurrentAuth = AuthList.begin(); CurrentAuth != AuthList.end();
 	      ++CurrentAuth)
-	    if (CurrentAuth->Host == Srv->ServerName.Host)
+	    if (CurrentAuth->Host == Srv->ServerName.Address.to_hostname())
 	    {
 	       AuthUser = CurrentAuth->User;
 	       AuthPass = CurrentAuth->Password;
@@ -957,7 +938,7 @@ int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv)
       // Nope - get username and password
       if (CurrentAuth == AuthList.end())
       {
-	 Description = ParsedURI.Host;
+	 Description = ParsedURI.Address.to_hostname();
 
 #ifdef USE_TLS
 	 if (ParsedURI.Access == "https")
@@ -969,13 +950,13 @@ int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv)
 	    // Got new credentials; save them
 	    AuthRec NewAuthInfo;
 
-	    NewAuthInfo.Host = Srv->ServerName.Host;
+	    NewAuthInfo.Host = Srv->ServerName.Address.to_hostname();
 	    NewAuthInfo.User = AuthUser;
 	    NewAuthInfo.Password = AuthPass;
 
 	    for (CurrentAuth = AuthList.begin(); CurrentAuth != AuthList.end();
 		 ++CurrentAuth)
-	       if (CurrentAuth->Host == Srv->ServerName.Host)
+	       if (CurrentAuth->Host == Srv->ServerName.Address.to_hostname())
 	       {
 		  *CurrentAuth = NewAuthInfo;
 		  break;
diff --git a/apt/methods/http.h b/apt/methods/http.h
index 9e4d2be..0a8362a 100644
--- a/apt/methods/http.h
+++ b/apt/methods/http.h
@@ -110,7 +110,7 @@ struct ServerState
    URI ServerName;
   
    bool HeaderLine(const string &Line);
-   bool Comp(URI Other) {return Other.Host == ServerName.Host && Other.Port == ServerName.Port;};
+   bool Comp(URI Other) {return Other.Address == ServerName.Address;};
    void Reset() {Major = 0; Minor = 0; Result = 0; Size = 0; StartPos = 0;
                  Encoding = Closes; time(&Date); ServerFd.reset();
                  Pipeline = true; };
diff --git a/apt/methods/rsh.cc b/apt/methods/rsh.cc
index 8bc47b0..2371e4a 100644
--- a/apt/methods/rsh.cc
+++ b/apt/methods/rsh.cc
@@ -77,7 +77,7 @@ bool RSHConn::Open()
    if (Process != -1)
       return true;
 
-   if (Connect(ServerName.Host,ServerName.User) == false)
+   if (Connect(ServerName.Address.to_hostname(),ServerName.User) == false)
       return false;
 
    return true;
@@ -428,7 +428,7 @@ bool RSHMethod::Fetch(FetchItem *Itm)
 
    // We say this mainly because the pause here is for the
    // ssh connection that is still going
-   Status(_("Connecting to %s"), Get.Host.c_str());
+   Status(_("Connecting to %s"), Get.Address.to_hostname().c_str());
 
    // Get the files information
    unsigned long long Size;
diff --git a/apt/methods/rsh.h b/apt/methods/rsh.h
index e99c59c..010a28f 100644
--- a/apt/methods/rsh.h
+++ b/apt/methods/rsh.h
@@ -33,7 +33,7 @@ class RSHConn
    // Raw connection IO
    bool WriteMsg(string &Text,bool Sync,const char *Fmt,...);
    bool Connect(const string &Host, const string &User);
-   bool Comp(URI Other) {return Other.Host == ServerName.Host && Other.Port == ServerName.Port;};
+   bool Comp(URI Other) {return Other.Address == ServerName.Address;};
 
    // Connection control
    bool Open();
diff --git a/apt/methods/rsync.cc b/apt/methods/rsync.cc
index efc1c0e..f28bb5a 100644
--- a/apt/methods/rsync.cc
+++ b/apt/methods/rsync.cc
@@ -353,11 +353,7 @@ bool RsyncMethod::RsyncConnExec::Get(pkgAcqMethod *Owner, FetchResult &FRes, con
 	  }
    }
 
-   char port[12];
-   if (srv.Port!=0)
-	  snprintf(port, sizeof(port), ":%u", srv.Port);
-   else port[0] = 0;
-   argv.add( "rsync://" + srv.Host + port + From);
+   argv.add( "rsync://" + srv.Address.to_string() + From);
    argv.add(To);
 
    if ( pipe(p) ) {
@@ -519,12 +515,12 @@ bool RsyncMethod::Fetch(FetchItem *Itm)
 	  Res.ResumePoint = st.st_size;
    }
 
-   string proxy = _config->Find(string("Acquire::rsync::proxy::")+Get.Host);
+   string proxy = _config->Find(string("Acquire::rsync::proxy::")+Get.Address.to_hostname());
    if ( proxy.empty() )
 	  proxy = _config->Find("Acquire::rsync::proxy");
 
    if (Debug)
-	  cerr << endl << "RSYNC: Proxy(" << Get.Host << "): " << proxy << endl;
+	  cerr << endl << "RSYNC: Proxy(" << Get.Address.to_hostname() << "): " << proxy << endl;
 
    // Don't compare now for the same server uri
    delete server;
diff --git a/apt/test/uri.cc b/apt/test/uri.cc
index ec652de..535aedb 100644
--- a/apt/test/uri.cc
+++ b/apt/test/uri.cc
@@ -7,9 +7,12 @@ void Test(const char *Foo)
 {
    URI U(Foo);
    
-   printf("%s a='%s' u='%s' p='%s' port='%u'\n   h='%s' p='%s'\n",
+   printf("%s a='%s' u='%s' p='%s'\n   h='%s' i='%s' port='%s' ipv6='%s'\n   p='%s'\n",
 	  Foo,U.Access.c_str(),U.User.c_str(),U.Password.c_str(),
-	  U.Port,U.Host.c_str(),U.Path.c_str());
+	  U.Address.hostname.c_str(), U.Address.interface.c_str(),
+	  U.Address.port ? std::to_string(*(U.Address.port)).c_str() : "(nil)",
+	  U.Address.is_ipv6addr ? "true" : "false",
+	  U.Path.c_str());
 }
 
 int main()
@@ -22,10 +25,17 @@ int main()
    Test("gzip:./bar/cow");
 	   
    // RFC 2732 stuff
+   Test("http://127.0.0.1/foo");
+   Test("http://127.0.0.1:80/foo");
    Test("http://[1080::8:800:200C:417A]/foo");
    Test("http://[::FFFF:129.144.52.38]:80/index.html");
    Test("http://[::FFFF:129.144.52.38:]:80/index.html");
    Test("http://[::FFFF:129.144.52.38:]/index.html");
+   Test("http://[::FFFF:129.144.52.38%eth0]/index.html");
+   Test("http://[::FFFF:129.144.52.38%]/index.html");
+   Test("http://[::FFFF:129.144.52.38%l]/index.html");
+   Test("http://[::FFFF:129.144.52.38]:/index.html");
+   Test("http://[::FFFF:129.144.52.38]:8/index.html");
    
    /* My Evil Corruption of RFC 2732 to handle CDROM names! Fun for 
       the whole family! */
-- 
2.24.0



Подробная информация о списке рассылки Devel