]>
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 static bool ConnectionAllowed(char const * const Service
, std::string
const &Host
)/*{{{*/
66 if (unlikely(Host
.empty())) // the only legal empty host (RFC2782 '.' target) is detected by caller
68 if (APT::String::Endswith(Host
, ".onion") && _config
->FindB("Acquire::BlockDotOnion", true))
70 // TRANSLATOR: %s is e.g. Tor's ".onion" which would likely fail or leak info (RFC7686)
71 _error
->Error(_("Direct connection to %s domains is blocked by default."), ".onion");
72 if (strcmp(Service
, "http") == 0)
73 _error
->Error(_("If you meant to use Tor remember to use %s instead of %s."), "tor+http", "http");
79 // DoConnect - Attempt a connect operation /*{{{*/
80 // ---------------------------------------------------------------------
81 /* This helper function attempts a connection to a single address. */
82 static bool DoConnect(struct addrinfo
*Addr
,std::string
const &Host
,
83 unsigned long TimeOut
,int &Fd
,pkgAcqMethod
*Owner
)
85 // Show a status indicator
86 char Name
[NI_MAXHOST
];
87 char Service
[NI_MAXSERV
];
91 getnameinfo(Addr
->ai_addr
,Addr
->ai_addrlen
,
92 Name
,sizeof(Name
),Service
,sizeof(Service
),
93 NI_NUMERICHOST
|NI_NUMERICSERV
);
94 Owner
->Status(_("Connecting to %s (%s)"),Host
.c_str(),Name
);
96 // if that addr did timeout before, we do not try it again
97 if(bad_addr
.find(std::string(Name
)) != bad_addr
.end())
100 /* If this is an IP rotation store the IP we are using.. If something goes
101 wrong this will get tacked onto the end of the error message */
102 if (LastHostAddr
->ai_next
!= 0)
104 std::stringstream ss
;
105 ioprintf(ss
, _("[IP: %s %s]"),Name
,Service
);
106 Owner
->SetIP(ss
.str());
110 if ((Fd
= socket(Addr
->ai_family
,Addr
->ai_socktype
,
111 Addr
->ai_protocol
)) < 0)
112 return _error
->Errno("socket",_("Could not create a socket for %s (f=%u t=%u p=%u)"),
113 Name
,Addr
->ai_family
,Addr
->ai_socktype
,Addr
->ai_protocol
);
115 SetNonBlock(Fd
,true);
116 if (connect(Fd
,Addr
->ai_addr
,Addr
->ai_addrlen
) < 0 &&
117 errno
!= EINPROGRESS
)
118 return _error
->Errno("connect",_("Cannot initiate the connection "
119 "to %s:%s (%s)."),Host
.c_str(),Service
,Name
);
121 /* This implements a timeout for connect by opening the connection
123 if (WaitFd(Fd
,true,TimeOut
) == false) {
124 bad_addr
.insert(bad_addr
.begin(), std::string(Name
));
125 Owner
->SetFailReason("Timeout");
126 return _error
->Error(_("Could not connect to %s:%s (%s), "
127 "connection timed out"),Host
.c_str(),Service
,Name
);
130 // Check the socket for an error condition
132 unsigned int Len
= sizeof(Err
);
133 if (getsockopt(Fd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
134 return _error
->Errno("getsockopt",_("Failed"));
139 if(errno
== ECONNREFUSED
)
140 Owner
->SetFailReason("ConnectionRefused");
141 else if (errno
== ETIMEDOUT
)
142 Owner
->SetFailReason("ConnectionTimedOut");
143 bad_addr
.insert(bad_addr
.begin(), std::string(Name
));
144 return _error
->Errno("connect",_("Could not connect to %s:%s (%s)."),Host
.c_str(),
151 // Connect to a given Hostname /*{{{*/
152 static bool ConnectToHostname(std::string
const &Host
, int const Port
,
153 const char * const Service
, int DefPort
, int &Fd
,
154 unsigned long const TimeOut
, pkgAcqMethod
* const Owner
)
156 if (ConnectionAllowed(Service
, Host
) == false)
158 // Convert the port name/number
161 snprintf(ServStr
,sizeof(ServStr
),"%i", Port
);
163 snprintf(ServStr
,sizeof(ServStr
),"%s", Service
);
165 /* We used a cached address record.. Yes this is against the spec but
166 the way we have setup our rotating dns suggests that this is more
168 if (LastHost
!= Host
|| LastPort
!= Port
)
170 Owner
->Status(_("Connecting to %s"),Host
.c_str());
172 // Free the old address structure
173 if (LastHostAddr
!= 0)
175 freeaddrinfo(LastHostAddr
);
180 // We only understand SOCK_STREAM sockets.
181 struct addrinfo Hints
;
182 memset(&Hints
,0,sizeof(Hints
));
183 Hints
.ai_socktype
= SOCK_STREAM
;
186 if (_config
->FindB("Acquire::Connect::IDN", true) == true)
187 Hints
.ai_flags
|= AI_IDN
;
189 // see getaddrinfo(3): only return address if system has such a address configured
190 // useful if system is ipv4 only, to not get ipv6, but that fails if the system has
191 // no address configured: e.g. offline and trying to connect to localhost.
192 if (_config
->FindB("Acquire::Connect::AddrConfig", true) == true)
193 Hints
.ai_flags
|= AI_ADDRCONFIG
;
194 Hints
.ai_protocol
= 0;
196 if(_config
->FindB("Acquire::ForceIPv4", false) == true)
197 Hints
.ai_family
= AF_INET
;
198 else if(_config
->FindB("Acquire::ForceIPv6", false) == true)
199 Hints
.ai_family
= AF_INET6
;
201 Hints
.ai_family
= AF_UNSPEC
;
203 // if we couldn't resolve the host before, we don't try now
204 if(bad_addr
.find(Host
) != bad_addr
.end())
205 return _error
->Error(_("Could not resolve '%s'"),Host
.c_str());
207 // Resolve both the host and service simultaneously
211 if ((Res
= getaddrinfo(Host
.c_str(),ServStr
,&Hints
,&LastHostAddr
)) != 0 ||
214 if (Res
== EAI_NONAME
|| Res
== EAI_SERVICE
)
218 snprintf(ServStr
, sizeof(ServStr
), "%i", DefPort
);
222 bad_addr
.insert(bad_addr
.begin(), Host
);
223 Owner
->SetFailReason("ResolveFailure");
224 return _error
->Error(_("Could not resolve '%s'"),Host
.c_str());
227 if (Res
== EAI_AGAIN
)
229 Owner
->SetFailReason("TmpResolveFailure");
230 return _error
->Error(_("Temporary failure resolving '%s'"),
233 if (Res
== EAI_SYSTEM
)
234 return _error
->Errno("getaddrinfo", _("System error resolving '%s:%s'"),
235 Host
.c_str(),ServStr
);
236 return _error
->Error(_("Something wicked happened resolving '%s:%s' (%i - %s)"),
237 Host
.c_str(),ServStr
,Res
,gai_strerror(Res
));
246 // When we have an IP rotation stay with the last IP.
247 struct addrinfo
*CurHost
= LastHostAddr
;
253 if (DoConnect(CurHost
,Host
,TimeOut
,Fd
,Owner
) == true)
261 // Ignore UNIX domain sockets
264 CurHost
= CurHost
->ai_next
;
266 while (CurHost
!= 0 && CurHost
->ai_family
== AF_UNIX
);
268 /* If we reached the end of the search list then wrap around to the
270 if (CurHost
== 0 && LastUsed
!= 0)
271 CurHost
= LastHostAddr
;
273 // Reached the end of the search cycle
274 if (CurHost
== LastUsed
)
281 if (_error
->PendingError() == true)
283 return _error
->Error(_("Unable to connect to %s:%s:"),Host
.c_str(),ServStr
);
286 // Connect - Connect to a server /*{{{*/
287 // ---------------------------------------------------------------------
288 /* Performs a connection to the server (including SRV record lookup) */
289 bool Connect(std::string Host
,int Port
,const char *Service
,
291 unsigned long TimeOut
,pkgAcqMethod
*Owner
)
293 if (_error
->PendingError() == true)
296 if (ConnectionAllowed(Service
, Host
) == false)
299 if(LastHost
!= Host
|| LastPort
!= Port
)
302 if (_config
->FindB("Acquire::EnableSrvRecords", true) == true)
304 GetSrvRecords(Host
, DefPort
, SrvRecords
);
305 // RFC2782 defines that a lonely '.' target is an abort reason
306 if (SrvRecords
.size() == 1 && SrvRecords
[0].target
.empty())
307 return _error
->Error("SRV records for %s indicate that "
308 "%s service is not available at this domain", Host
.c_str(), Service
);
312 size_t stackSize
= 0;
313 // try to connect in the priority order of the srv records
314 std::string initialHost
{std::move(Host
)};
315 while(SrvRecords
.empty() == false)
317 _error
->PushToStack();
319 // PopFromSrvRecs will also remove the server
320 Host
= PopFromSrvRecs(SrvRecords
).target
;
321 auto const ret
= ConnectToHostname(Host
, Port
, Service
, DefPort
, Fd
, TimeOut
, Owner
);
325 _error
->RevertToStack();
329 Host
= std::move(initialHost
);
331 // we have no (good) SrvRecords for this host, connect right away
332 _error
->PushToStack();
334 auto const ret
= ConnectToHostname(Host
, Port
, Service
, DefPort
, Fd
,
338 _error
->RevertToStack();
340 _error
->MergeWithStack();