]> git.saurik.com Git - apt.git/blob - methods/connect.cc
Add apt-pkg/tagfile-keys.cc to the gitignore file
[apt.git] / methods / connect.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: connect.cc,v 1.10.2.1 2004/01/16 18:58:50 mdz Exp $
4 /* ######################################################################
5
6 Connect - Replacement connect call
7
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.
10
11 ##################################################################### */
12 /*}}}*/
13 // Include Files /*{{{*/
14 #include <config.h>
15
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>
22
23 #include <stdio.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <sstream>
27 #include <string.h>
28 #include<set>
29 #include<string>
30
31 // Internet stuff
32 #include <netinet/in.h>
33 #include <sys/socket.h>
34 #include <arpa/inet.h>
35 #include <netdb.h>
36
37 #include "connect.h"
38 #include "rfc2553emu.h"
39 #include <apti18n.h>
40 /*}}}*/
41
42 static std::string LastHost;
43 static int LastPort = 0;
44 static struct addrinfo *LastHostAddr = 0;
45 static struct addrinfo *LastUsed = 0;
46
47 static std::vector<SrvRec> SrvRecords;
48
49 // Set of IP/hostnames that we timed out before or couldn't resolve
50 static std::set<std::string> bad_addr;
51
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
55 new server */
56 void RotateDNS()
57 {
58 if (LastUsed != 0 && LastUsed->ai_next != 0)
59 LastUsed = LastUsed->ai_next;
60 else
61 LastUsed = LastHostAddr;
62 }
63 /*}}}*/
64 static bool ConnectionAllowed(char const * const Service, std::string const &Host)/*{{{*/
65 {
66 if (unlikely(Host.empty())) // the only legal empty host (RFC2782 '.' target) is detected by caller
67 return false;
68 if (APT::String::Endswith(Host, ".onion") && _config->FindB("Acquire::BlockDotOnion", true))
69 {
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");
74 return false;
75 }
76 return true;
77 }
78 /*}}}*/
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)
84 {
85 // Show a status indicator
86 char Name[NI_MAXHOST];
87 char Service[NI_MAXSERV];
88
89 Name[0] = 0;
90 Service[0] = 0;
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);
95
96 // if that addr did timeout before, we do not try it again
97 if(bad_addr.find(std::string(Name)) != bad_addr.end())
98 return false;
99
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)
103 {
104 std::stringstream ss;
105 ioprintf(ss, _("[IP: %s %s]"),Name,Service);
106 Owner->SetIP(ss.str());
107 }
108
109 // Get a socket
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);
114
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);
120
121 /* This implements a timeout for connect by opening the connection
122 nonblocking */
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);
128 }
129
130 // Check the socket for an error condition
131 unsigned int Err;
132 unsigned int Len = sizeof(Err);
133 if (getsockopt(Fd,SOL_SOCKET,SO_ERROR,&Err,&Len) != 0)
134 return _error->Errno("getsockopt",_("Failed"));
135
136 if (Err != 0)
137 {
138 errno = Err;
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(),
145 Service,Name);
146 }
147
148 return true;
149 }
150 /*}}}*/
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)
155 {
156 if (ConnectionAllowed(Service, Host) == false)
157 return false;
158 // Convert the port name/number
159 char ServStr[300];
160 if (Port != 0)
161 snprintf(ServStr,sizeof(ServStr),"%i", Port);
162 else
163 snprintf(ServStr,sizeof(ServStr),"%s", Service);
164
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
167 sensible */
168 if (LastHost != Host || LastPort != Port)
169 {
170 Owner->Status(_("Connecting to %s"),Host.c_str());
171
172 // Free the old address structure
173 if (LastHostAddr != 0)
174 {
175 freeaddrinfo(LastHostAddr);
176 LastHostAddr = 0;
177 LastUsed = 0;
178 }
179
180 // We only understand SOCK_STREAM sockets.
181 struct addrinfo Hints;
182 memset(&Hints,0,sizeof(Hints));
183 Hints.ai_socktype = SOCK_STREAM;
184 Hints.ai_flags = 0;
185 #ifdef AI_IDN
186 if (_config->FindB("Acquire::Connect::IDN", true) == true)
187 Hints.ai_flags |= AI_IDN;
188 #endif
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;
195
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;
200 else
201 Hints.ai_family = AF_UNSPEC;
202
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());
206
207 // Resolve both the host and service simultaneously
208 while (1)
209 {
210 int Res;
211 if ((Res = getaddrinfo(Host.c_str(),ServStr,&Hints,&LastHostAddr)) != 0 ||
212 LastHostAddr == 0)
213 {
214 if (Res == EAI_NONAME || Res == EAI_SERVICE)
215 {
216 if (DefPort != 0)
217 {
218 snprintf(ServStr, sizeof(ServStr), "%i", DefPort);
219 DefPort = 0;
220 continue;
221 }
222 bad_addr.insert(bad_addr.begin(), Host);
223 Owner->SetFailReason("ResolveFailure");
224 return _error->Error(_("Could not resolve '%s'"),Host.c_str());
225 }
226
227 if (Res == EAI_AGAIN)
228 {
229 Owner->SetFailReason("TmpResolveFailure");
230 return _error->Error(_("Temporary failure resolving '%s'"),
231 Host.c_str());
232 }
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));
238 }
239 break;
240 }
241
242 LastHost = Host;
243 LastPort = Port;
244 }
245
246 // When we have an IP rotation stay with the last IP.
247 struct addrinfo *CurHost = LastHostAddr;
248 if (LastUsed != 0)
249 CurHost = LastUsed;
250
251 while (CurHost != 0)
252 {
253 if (DoConnect(CurHost,Host,TimeOut,Fd,Owner) == true)
254 {
255 LastUsed = CurHost;
256 return true;
257 }
258 close(Fd);
259 Fd = -1;
260
261 // Ignore UNIX domain sockets
262 do
263 {
264 CurHost = CurHost->ai_next;
265 }
266 while (CurHost != 0 && CurHost->ai_family == AF_UNIX);
267
268 /* If we reached the end of the search list then wrap around to the
269 start */
270 if (CurHost == 0 && LastUsed != 0)
271 CurHost = LastHostAddr;
272
273 // Reached the end of the search cycle
274 if (CurHost == LastUsed)
275 break;
276
277 if (CurHost != 0)
278 _error->Discard();
279 }
280
281 if (_error->PendingError() == true)
282 return false;
283 return _error->Error(_("Unable to connect to %s:%s:"),Host.c_str(),ServStr);
284 }
285 /*}}}*/
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,
290 int DefPort,int &Fd,
291 unsigned long TimeOut,pkgAcqMethod *Owner)
292 {
293 if (_error->PendingError() == true)
294 return false;
295
296 if (ConnectionAllowed(Service, Host) == false)
297 return false;
298
299 if(LastHost != Host || LastPort != Port)
300 {
301 SrvRecords.clear();
302 if (_config->FindB("Acquire::EnableSrvRecords", true) == true)
303 {
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);
309 }
310 }
311
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)
316 {
317 _error->PushToStack();
318 ++stackSize;
319 // PopFromSrvRecs will also remove the server
320 Host = PopFromSrvRecs(SrvRecords).target;
321 auto const ret = ConnectToHostname(Host, Port, Service, DefPort, Fd, TimeOut, Owner);
322 if (ret)
323 {
324 while(stackSize--)
325 _error->RevertToStack();
326 return true;
327 }
328 }
329 Host = std::move(initialHost);
330
331 // we have no (good) SrvRecords for this host, connect right away
332 _error->PushToStack();
333 ++stackSize;
334 auto const ret = ConnectToHostname(Host, Port, Service, DefPort, Fd,
335 TimeOut, Owner);
336 while(stackSize--)
337 if (ret)
338 _error->RevertToStack();
339 else
340 _error->MergeWithStack();
341 return ret;
342 }