]>
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 /*{{{*/
15 #include <apt-pkg/error.h>
16 #include <apt-pkg/fileutl.h>
26 #include <netinet/in.h>
27 #include <sys/socket.h>
28 #include <arpa/inet.h>
31 #include "rfc2553emu.h"
35 static string LastHost
;
36 static int LastPort
= 0;
37 static struct addrinfo
*LastHostAddr
= 0;
38 static struct addrinfo
*LastUsed
= 0;
40 // Set of IP/hostnames that we timed out before or couldn't resolve
41 static std::set
<string
> bad_addr
;
43 // RotateDNS - Select a new server from a DNS rotation /*{{{*/
44 // ---------------------------------------------------------------------
45 /* This is called during certain errors in order to recover by selecting a
49 if (LastUsed
!= 0 && LastUsed
->ai_next
!= 0)
50 LastUsed
= LastUsed
->ai_next
;
52 LastUsed
= LastHostAddr
;
55 // DoConnect - Attempt a connect operation /*{{{*/
56 // ---------------------------------------------------------------------
57 /* This helper function attempts a connection to a single address. */
58 static bool DoConnect(struct addrinfo
*Addr
,string Host
,
59 unsigned long TimeOut
,int &Fd
,pkgAcqMethod
*Owner
)
61 // Show a status indicator
62 char Name
[NI_MAXHOST
];
63 char Service
[NI_MAXSERV
];
67 getnameinfo(Addr
->ai_addr
,Addr
->ai_addrlen
,
68 Name
,sizeof(Name
),Service
,sizeof(Service
),
69 NI_NUMERICHOST
|NI_NUMERICSERV
);
70 Owner
->Status(_("Connecting to %s (%s)"),Host
.c_str(),Name
);
72 // if that addr did timeout before, we do not try it again
73 if(bad_addr
.find(string(Name
)) != bad_addr
.end())
76 /* If this is an IP rotation store the IP we are using.. If something goes
77 wrong this will get tacked onto the end of the error message */
78 if (LastHostAddr
->ai_next
!= 0)
80 char Name2
[NI_MAXHOST
+ NI_MAXSERV
+ 10];
81 snprintf(Name2
,sizeof(Name2
),_("[IP: %s %s]"),Name
,Service
);
82 Owner
->SetFailExtraMsg(string(Name2
));
85 Owner
->SetFailExtraMsg("");
88 if ((Fd
= socket(Addr
->ai_family
,Addr
->ai_socktype
,
89 Addr
->ai_protocol
)) < 0)
90 return _error
->Errno("socket",_("Could not create a socket for %s (f=%u t=%u p=%u)"),
91 Name
,Addr
->ai_family
,Addr
->ai_socktype
,Addr
->ai_protocol
);
94 if (connect(Fd
,Addr
->ai_addr
,Addr
->ai_addrlen
) < 0 &&
96 return _error
->Errno("connect",_("Cannot initiate the connection "
97 "to %s:%s (%s)."),Host
.c_str(),Service
,Name
);
99 /* This implements a timeout for connect by opening the connection
101 if (WaitFd(Fd
,true,TimeOut
) == false) {
102 bad_addr
.insert(bad_addr
.begin(), string(Name
));
103 Owner
->SetFailExtraMsg("\nFailReason: Timeout");
104 return _error
->Error(_("Could not connect to %s:%s (%s), "
105 "connection timed out"),Host
.c_str(),Service
,Name
);
108 // Check the socket for an error condition
110 unsigned int Len
= sizeof(Err
);
111 if (getsockopt(Fd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
112 return _error
->Errno("getsockopt",_("Failed"));
117 if(errno
== ECONNREFUSED
)
118 Owner
->SetFailExtraMsg("\nFailReason: ConnectionRefused");
119 return _error
->Errno("connect",_("Could not connect to %s:%s (%s)."),Host
.c_str(),
126 // Connect - Connect to a server /*{{{*/
127 // ---------------------------------------------------------------------
128 /* Performs a connection to the server */
129 bool Connect(string Host
,int Port
,const char *Service
,int DefPort
,int &Fd
,
130 unsigned long TimeOut
,pkgAcqMethod
*Owner
)
132 if (_error
->PendingError() == true)
135 // Convert the port name/number
138 snprintf(ServStr
,sizeof(ServStr
),"%u",Port
);
140 snprintf(ServStr
,sizeof(ServStr
),"%s",Service
);
142 /* We used a cached address record.. Yes this is against the spec but
143 the way we have setup our rotating dns suggests that this is more
145 if (LastHost
!= Host
|| LastPort
!= Port
)
147 Owner
->Status(_("Connecting to %s"),Host
.c_str());
149 // Free the old address structure
150 if (LastHostAddr
!= 0)
152 freeaddrinfo(LastHostAddr
);
157 // We only understand SOCK_STREAM sockets.
158 struct addrinfo Hints
;
159 memset(&Hints
,0,sizeof(Hints
));
160 Hints
.ai_socktype
= SOCK_STREAM
;
161 Hints
.ai_protocol
= 0;
163 // if we couldn't resolve the host before, we don't try now
164 if(bad_addr
.find(Host
) != bad_addr
.end())
165 return _error
->Error(_("Could not resolve '%s'"),Host
.c_str());
167 // Resolve both the host and service simultaneously
171 if ((Res
= getaddrinfo(Host
.c_str(),ServStr
,&Hints
,&LastHostAddr
)) != 0 ||
174 if (Res
== EAI_NONAME
|| Res
== EAI_SERVICE
)
178 snprintf(ServStr
,sizeof(ServStr
),"%u",DefPort
);
182 bad_addr
.insert(bad_addr
.begin(), Host
);
183 Owner
->SetFailExtraMsg("\nFailReason: ResolveFailure");
184 return _error
->Error(_("Could not resolve '%s'"),Host
.c_str());
187 if (Res
== EAI_AGAIN
)
189 Owner
->SetFailExtraMsg("\nFailReason: TmpResolveFailure");
190 return _error
->Error(_("Temporary failure resolving '%s'"),
193 return _error
->Error(_("Something wicked happened resolving '%s:%s' (%i - %s)"),
194 Host
.c_str(),ServStr
,Res
,gai_strerror(Res
));
203 // When we have an IP rotation stay with the last IP.
204 struct addrinfo
*CurHost
= LastHostAddr
;
210 if (DoConnect(CurHost
,Host
,TimeOut
,Fd
,Owner
) == true)
218 // Ignore UNIX domain sockets
221 CurHost
= CurHost
->ai_next
;
223 while (CurHost
!= 0 && CurHost
->ai_family
== AF_UNIX
);
225 /* If we reached the end of the search list then wrap around to the
227 if (CurHost
== 0 && LastUsed
!= 0)
228 CurHost
= LastHostAddr
;
230 // Reached the end of the search cycle
231 if (CurHost
== LastUsed
)
238 if (_error
->PendingError() == true)
240 return _error
->Error(_("Unable to connect to %s:%s:"),Host
.c_str(),ServStr
);