]>
git.saurik.com Git - apt.git/blob - methods/connect.cc
   1 // -*- mode: cpp; mode: fold -*- 
   3 // $Id: connect.cc,v 1.10.2.1 2004/01/16 18:58:50 mdz Exp $ 
   4 /* ###################################################################### 
   6    Connect - Replacement connect call 
   8    This was originally authored by Jason Gunthorpe <jgg@debian.org> 
   9    and is placed in the Public Domain, do with it what you will. 
  11    ##################################################################### */ 
  13 // Include Files                                                        /*{{{*/ 
  16 #include <apt-pkg/error.h> 
  17 #include <apt-pkg/fileutl.h> 
  18 #include <apt-pkg/strutl.h> 
  19 #include <apt-pkg/acquire-method.h> 
  20 #include <apt-pkg/configuration.h> 
  21 #include <apt-pkg/srvrec.h> 
  32 #include <netinet/in.h> 
  33 #include <sys/socket.h> 
  34 #include <arpa/inet.h> 
  38 #include "rfc2553emu.h" 
  42 static std::string LastHost
; 
  43 static int LastPort 
= 0; 
  44 static struct addrinfo 
*LastHostAddr 
= 0; 
  45 static struct addrinfo 
*LastUsed 
= 0; 
  47 static std::vector
<SrvRec
> SrvRecords
; 
  49 // Set of IP/hostnames that we timed out before or couldn't resolve 
  50 static std::set
<std::string
> bad_addr
; 
  52 // RotateDNS - Select a new server from a DNS rotation                  /*{{{*/ 
  53 // --------------------------------------------------------------------- 
  54 /* This is called during certain errors in order to recover by selecting a  
  58    if (LastUsed 
!= 0 && LastUsed
->ai_next 
!= 0) 
  59       LastUsed 
= LastUsed
->ai_next
; 
  61       LastUsed 
= LastHostAddr
; 
  64 // DoConnect - Attempt a connect operation                              /*{{{*/ 
  65 // --------------------------------------------------------------------- 
  66 /* This helper function attempts a connection to a single address. */ 
  67 static bool DoConnect(struct addrinfo 
*Addr
,std::string Host
, 
  68                       unsigned long TimeOut
,int &Fd
,pkgAcqMethod 
*Owner
) 
  70    // Show a status indicator 
  71    char Name
[NI_MAXHOST
]; 
  72    char Service
[NI_MAXSERV
]; 
  76    getnameinfo(Addr
->ai_addr
,Addr
->ai_addrlen
, 
  77                Name
,sizeof(Name
),Service
,sizeof(Service
), 
  78                NI_NUMERICHOST
|NI_NUMERICSERV
); 
  79    Owner
->Status(_("Connecting to %s (%s)"),Host
.c_str(),Name
); 
  81    // if that addr did timeout before, we do not try it again 
  82    if(bad_addr
.find(std::string(Name
)) != bad_addr
.end()) 
  85    /* If this is an IP rotation store the IP we are using.. If something goes 
  86       wrong this will get tacked onto the end of the error message */ 
  87    if (LastHostAddr
->ai_next 
!= 0) 
  90       ioprintf(ss
, _("[IP: %s %s]"),Name
,Service
); 
  91       Owner
->SetIP(ss
.str()); 
  95    if ((Fd 
= socket(Addr
->ai_family
,Addr
->ai_socktype
, 
  96                     Addr
->ai_protocol
)) < 0) 
  97       return _error
->Errno("socket",_("Could not create a socket for %s (f=%u t=%u p=%u)"), 
  98                            Name
,Addr
->ai_family
,Addr
->ai_socktype
,Addr
->ai_protocol
); 
 100    SetNonBlock(Fd
,true); 
 101    if (connect(Fd
,Addr
->ai_addr
,Addr
->ai_addrlen
) < 0 && 
 102        errno 
!= EINPROGRESS
) 
 103       return _error
->Errno("connect",_("Cannot initiate the connection " 
 104                            "to %s:%s (%s)."),Host
.c_str(),Service
,Name
); 
 106    /* This implements a timeout for connect by opening the connection 
 108    if (WaitFd(Fd
,true,TimeOut
) == false) { 
 109       bad_addr
.insert(bad_addr
.begin(), std::string(Name
)); 
 110       Owner
->SetFailReason("Timeout"); 
 111       return _error
->Error(_("Could not connect to %s:%s (%s), " 
 112                            "connection timed out"),Host
.c_str(),Service
,Name
); 
 115    // Check the socket for an error condition 
 117    unsigned int Len 
= sizeof(Err
); 
 118    if (getsockopt(Fd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0) 
 119       return _error
->Errno("getsockopt",_("Failed")); 
 124       if(errno 
== ECONNREFUSED
) 
 125          Owner
->SetFailReason("ConnectionRefused"); 
 126       else if (errno 
== ETIMEDOUT
) 
 127          Owner
->SetFailReason("ConnectionTimedOut"); 
 128       bad_addr
.insert(bad_addr
.begin(), std::string(Name
)); 
 129       return _error
->Errno("connect",_("Could not connect to %s:%s (%s)."),Host
.c_str(), 
 136 // Connect to a given Hostname                                          /*{{{*/ 
 137 static bool ConnectToHostname(std::string 
const &Host
, int const Port
, 
 138       const char * const Service
, int DefPort
, int &Fd
, 
 139       unsigned long const TimeOut
, pkgAcqMethod 
* const Owner
) 
 141    // Convert the port name/number 
 144       snprintf(ServStr
,sizeof(ServStr
),"%i", Port
); 
 146       snprintf(ServStr
,sizeof(ServStr
),"%s", Service
); 
 148    /* We used a cached address record.. Yes this is against the spec but 
 149       the way we have setup our rotating dns suggests that this is more 
 151    if (LastHost 
!= Host 
|| LastPort 
!= Port
) 
 153       Owner
->Status(_("Connecting to %s"),Host
.c_str()); 
 155       // Free the old address structure 
 156       if (LastHostAddr 
!= 0) 
 158          freeaddrinfo(LastHostAddr
); 
 163       // We only understand SOCK_STREAM sockets. 
 164       struct addrinfo Hints
; 
 165       memset(&Hints
,0,sizeof(Hints
)); 
 166       Hints
.ai_socktype 
= SOCK_STREAM
; 
 168       if (_config
->FindB("Acquire::Connect::IDN", true) == true) 
 169          Hints
.ai_flags 
|= AI_IDN
; 
 170       // see getaddrinfo(3): only return address if system has such a address configured 
 171       // useful if system is ipv4 only, to not get ipv6, but that fails if the system has 
 172       // no address configured: e.g. offline and trying to connect to localhost. 
 173       if (_config
->FindB("Acquire::Connect::AddrConfig", true) == true) 
 174          Hints
.ai_flags 
|= AI_ADDRCONFIG
; 
 175       Hints
.ai_protocol 
= 0; 
 177       if(_config
->FindB("Acquire::ForceIPv4", false) == true) 
 178          Hints
.ai_family 
= AF_INET
; 
 179       else if(_config
->FindB("Acquire::ForceIPv6", false) == true) 
 180          Hints
.ai_family 
= AF_INET6
; 
 182          Hints
.ai_family 
= AF_UNSPEC
; 
 184       // if we couldn't resolve the host before, we don't try now 
 185       if(bad_addr
.find(Host
) != bad_addr
.end())  
 186          return _error
->Error(_("Could not resolve '%s'"),Host
.c_str()); 
 188       // Resolve both the host and service simultaneously 
 192          if ((Res 
= getaddrinfo(Host
.c_str(),ServStr
,&Hints
,&LastHostAddr
)) != 0 || 
 195             if (Res 
== EAI_NONAME 
|| Res 
== EAI_SERVICE
) 
 199                   snprintf(ServStr
, sizeof(ServStr
), "%i", DefPort
); 
 203                bad_addr
.insert(bad_addr
.begin(), Host
); 
 204                Owner
->SetFailReason("ResolveFailure"); 
 205                return _error
->Error(_("Could not resolve '%s'"),Host
.c_str()); 
 208             if (Res 
== EAI_AGAIN
) 
 210                Owner
->SetFailReason("TmpResolveFailure"); 
 211                return _error
->Error(_("Temporary failure resolving '%s'"), 
 214             if (Res 
== EAI_SYSTEM
) 
 215                return _error
->Errno("getaddrinfo", _("System error resolving '%s:%s'"), 
 216                                       Host
.c_str(),ServStr
); 
 217             return _error
->Error(_("Something wicked happened resolving '%s:%s' (%i - %s)"), 
 218                                  Host
.c_str(),ServStr
,Res
,gai_strerror(Res
)); 
 227    // When we have an IP rotation stay with the last IP. 
 228    struct addrinfo 
*CurHost 
= LastHostAddr
; 
 234       if (DoConnect(CurHost
,Host
,TimeOut
,Fd
,Owner
) == true) 
 242       // Ignore UNIX domain sockets 
 245          CurHost 
= CurHost
->ai_next
; 
 247       while (CurHost 
!= 0 && CurHost
->ai_family 
== AF_UNIX
); 
 249       /* If we reached the end of the search list then wrap around to the 
 251       if (CurHost 
== 0 && LastUsed 
!= 0) 
 252          CurHost 
= LastHostAddr
; 
 254       // Reached the end of the search cycle 
 255       if (CurHost 
== LastUsed
) 
 262    if (_error
->PendingError() == true) 
 264    return _error
->Error(_("Unable to connect to %s:%s:"),Host
.c_str(),ServStr
); 
 267 // Connect - Connect to a server                                        /*{{{*/ 
 268 // --------------------------------------------------------------------- 
 269 /* Performs a connection to the server (including SRV record lookup) */ 
 270 bool Connect(std::string Host
,int Port
,const char *Service
, 
 272                             unsigned long TimeOut
,pkgAcqMethod 
*Owner
) 
 274    if (_error
->PendingError() == true) 
 277    if(LastHost 
!= Host 
|| LastPort 
!= Port
) 
 280       if (_config
->FindB("Acquire::EnableSrvRecords", true) == true) 
 281          GetSrvRecords(Host
, DefPort
, SrvRecords
); 
 283    // we have no SrvRecords for this host, connect right away 
 284    if(SrvRecords
.size() == 0) 
 285       return ConnectToHostname(Host
, Port
, Service
, DefPort
, Fd
,  
 288    // try to connect in the priority order of the srv records 
 289    while(SrvRecords
.size() > 0) 
 291       // PopFromSrvRecs will also remove the server 
 292       Host 
= PopFromSrvRecs(SrvRecords
).target
; 
 293       if(ConnectToHostname(Host
, Port
, Service
, DefPort
, Fd
, TimeOut
, Owner
))