* get the mirror failure stuff really working
[ntk/apt.git] / methods / connect.cc
CommitLineData
0837bd25
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
7db98ffc 3// $Id: connect.cc,v 1.10.2.1 2004/01/16 18:58:50 mdz Exp $
0837bd25
AL
4/* ######################################################################
5
6 Connect - Replacement connect call
7da2b375
AL
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
0837bd25
AL
11 ##################################################################### */
12 /*}}}*/
13// Include Files /*{{{*/
14#include "connect.h"
15#include <apt-pkg/error.h>
16#include <apt-pkg/fileutl.h>
17
18#include <stdio.h>
19#include <errno.h>
20#include <unistd.h>
36280399 21#include <sstream>
0837bd25
AL
22
23// Internet stuff
24#include <netinet/in.h>
25#include <sys/socket.h>
26#include <arpa/inet.h>
27#include <netdb.h>
28
29#include "rfc2553emu.h"
d77559ac 30#include <apti18n.h>
0837bd25
AL
31 /*}}}*/
32
33static string LastHost;
34static int LastPort = 0;
35static struct addrinfo *LastHostAddr = 0;
36static struct addrinfo *LastUsed = 0;
37
b2e465d6
AL
38// RotateDNS - Select a new server from a DNS rotation /*{{{*/
39// ---------------------------------------------------------------------
40/* This is called during certain errors in order to recover by selecting a
41 new server */
42void RotateDNS()
43{
44 if (LastUsed != 0 && LastUsed->ai_next != 0)
45 LastUsed = LastUsed->ai_next;
46 else
47 LastUsed = LastHostAddr;
48}
49 /*}}}*/
0837bd25
AL
50// DoConnect - Attempt a connect operation /*{{{*/
51// ---------------------------------------------------------------------
52/* This helper function attempts a connection to a single address. */
53static bool DoConnect(struct addrinfo *Addr,string Host,
54 unsigned long TimeOut,int &Fd,pkgAcqMethod *Owner)
55{
56 // Show a status indicator
57 char Name[NI_MAXHOST];
28006885 58 char Service[NI_MAXSERV];
b2e465d6
AL
59
60 Name[0] = 0;
28006885 61 Service[0] = 0;
0837bd25 62 getnameinfo(Addr->ai_addr,Addr->ai_addrlen,
28006885
AL
63 Name,sizeof(Name),Service,sizeof(Service),
64 NI_NUMERICHOST|NI_NUMERICSERV);
dc738e7a 65 Owner->Status(_("Connecting to %s (%s)"),Host.c_str(),Name);
b2e465d6
AL
66
67 /* If this is an IP rotation store the IP we are using.. If something goes
68 wrong this will get tacked onto the end of the error message */
69 if (LastHostAddr->ai_next != 0)
70 {
36280399
MV
71 std::stringstream ss;
72 ioprintf(ss, _("[IP: %s %s]"),Name,Service);
73 Owner->SetIP(ss.str());
74 }
b2e465d6 75
0837bd25
AL
76 // Get a socket
77 if ((Fd = socket(Addr->ai_family,Addr->ai_socktype,
78 Addr->ai_protocol)) < 0)
dc738e7a 79 return _error->Errno("socket",_("Could not create a socket for %s (f=%u t=%u p=%u)"),
b2e465d6 80 Name,Addr->ai_family,Addr->ai_socktype,Addr->ai_protocol);
0837bd25
AL
81
82 SetNonBlock(Fd,true);
83 if (connect(Fd,Addr->ai_addr,Addr->ai_addrlen) < 0 &&
84 errno != EINPROGRESS)
dc738e7a
AL
85 return _error->Errno("connect",_("Cannot initiate the connection "
86 "to %s:%s (%s)."),Host.c_str(),Service,Name);
0837bd25
AL
87
88 /* This implements a timeout for connect by opening the connection
89 nonblocking */
24057ad6 90 if (WaitFd(Fd,true,TimeOut) == false) {
36280399 91 Owner->SetFailReason("Timeout");
dc738e7a
AL
92 return _error->Error(_("Could not connect to %s:%s (%s), "
93 "connection timed out"),Host.c_str(),Service,Name);
24057ad6 94 }
b2e465d6 95
0837bd25
AL
96 // Check the socket for an error condition
97 unsigned int Err;
98 unsigned int Len = sizeof(Err);
99 if (getsockopt(Fd,SOL_SOCKET,SO_ERROR,&Err,&Len) != 0)
dc738e7a 100 return _error->Errno("getsockopt",_("Failed"));
0837bd25
AL
101
102 if (Err != 0)
28006885
AL
103 {
104 errno = Err;
75dd8af1 105 if(errno == ECONNREFUSED)
36280399 106 Owner->SetFailReason("ConnectionRefused");
dc738e7a 107 return _error->Errno("connect",_("Could not connect to %s:%s (%s)."),Host.c_str(),
28006885
AL
108 Service,Name);
109 }
110
0837bd25
AL
111 return true;
112}
113 /*}}}*/
c141b9a9 114// Connect - Connect to a server /*{{{*/
0837bd25
AL
115// ---------------------------------------------------------------------
116/* Performs a connection to the server */
9505213b 117bool Connect(string Host,int Port,const char *Service,int DefPort,int &Fd,
0837bd25
AL
118 unsigned long TimeOut,pkgAcqMethod *Owner)
119{
120 if (_error->PendingError() == true)
121 return false;
28006885
AL
122
123 // Convert the port name/number
124 char ServStr[300];
125 if (Port != 0)
126 snprintf(ServStr,sizeof(ServStr),"%u",Port);
127 else
128 snprintf(ServStr,sizeof(ServStr),"%s",Service);
0837bd25
AL
129
130 /* We used a cached address record.. Yes this is against the spec but
131 the way we have setup our rotating dns suggests that this is more
132 sensible */
133 if (LastHost != Host || LastPort != Port)
134 {
dc738e7a 135 Owner->Status(_("Connecting to %s"),Host.c_str());
0837bd25 136
0837bd25
AL
137 // Free the old address structure
138 if (LastHostAddr != 0)
139 {
140 freeaddrinfo(LastHostAddr);
141 LastHostAddr = 0;
28006885 142 LastUsed = 0;
0837bd25
AL
143 }
144
145 // We only understand SOCK_STREAM sockets.
146 struct addrinfo Hints;
147 memset(&Hints,0,sizeof(Hints));
148 Hints.ai_socktype = SOCK_STREAM;
28006885 149 Hints.ai_protocol = 0;
0837bd25
AL
150
151 // Resolve both the host and service simultaneously
9505213b 152 while (1)
c141b9a9 153 {
9505213b 154 int Res;
28006885 155 if ((Res = getaddrinfo(Host.c_str(),ServStr,&Hints,&LastHostAddr)) != 0 ||
9505213b
AL
156 LastHostAddr == 0)
157 {
72472b95 158 if (Res == EAI_NONAME || Res == EAI_SERVICE)
9505213b
AL
159 {
160 if (DefPort != 0)
161 {
28006885 162 snprintf(ServStr,sizeof(ServStr),"%u",DefPort);
9505213b
AL
163 DefPort = 0;
164 continue;
165 }
dc738e7a 166 return _error->Error(_("Could not resolve '%s'"),Host.c_str());
9505213b
AL
167 }
168
4fe6e0c2 169 if (Res == EAI_AGAIN)
25182152 170 {
36280399 171 Owner->SetFailReason("TmpResolveFailure");
dc738e7a 172 return _error->Error(_("Temporary failure resolving '%s'"),
4fe6e0c2 173 Host.c_str());
25182152 174 }
dc738e7a 175 return _error->Error(_("Something wicked happened resolving '%s:%s' (%i)"),
b2e465d6 176 Host.c_str(),ServStr,Res);
9505213b
AL
177 }
178 break;
c141b9a9
AL
179 }
180
0837bd25
AL
181 LastHost = Host;
182 LastPort = Port;
0837bd25
AL
183 }
184
28006885 185 // When we have an IP rotation stay with the last IP.
0837bd25
AL
186 struct addrinfo *CurHost = LastHostAddr;
187 if (LastUsed != 0)
188 CurHost = LastUsed;
189
190 while (CurHost != 0)
191 {
192 if (DoConnect(CurHost,Host,TimeOut,Fd,Owner) == true)
193 {
194 LastUsed = CurHost;
195 return true;
196 }
197 close(Fd);
198 Fd = -1;
199
28006885
AL
200 // Ignore UNIX domain sockets
201 do
202 {
203 CurHost = CurHost->ai_next;
204 }
205 while (CurHost != 0 && CurHost->ai_family == AF_UNIX);
b2e465d6
AL
206
207 /* If we reached the end of the search list then wrap around to the
208 start */
209 if (CurHost == 0 && LastUsed != 0)
210 CurHost = LastHostAddr;
211
212 // Reached the end of the search cycle
213 if (CurHost == LastUsed)
214 break;
215
0837bd25
AL
216 if (CurHost != 0)
217 _error->Discard();
b2e465d6 218 }
28006885 219
dd1fd92b 220 if (_error->PendingError() == true)
b2e465d6 221 return false;
dc738e7a 222 return _error->Error(_("Unable to connect to %s %s:"),Host.c_str(),ServStr);
0837bd25
AL
223}
224 /*}}}*/