| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- // -*- mode: cpp; mode: fold -*-
- // Description /*{{{*/
- // $Id: connect.cc,v 1.5 2000/05/12 05:04:57 jgg Exp $
- /* ######################################################################
- Connect - Replacement connect call
-
- ##################################################################### */
- /*}}}*/
- // Include Files /*{{{*/
- #include "connect.h"
- #include <apt-pkg/error.h>
- #include <apt-pkg/fileutl.h>
- #include <stdio.h>
- #include <errno.h>
- #include <unistd.h>
- // Internet stuff
- #include <netinet/in.h>
- #include <sys/socket.h>
- #include <arpa/inet.h>
- #include <netdb.h>
- #include "rfc2553emu.h"
- /*}}}*/
- static string LastHost;
- static int LastPort = 0;
- static struct addrinfo *LastHostAddr = 0;
- static struct addrinfo *LastUsed = 0;
- // DoConnect - Attempt a connect operation /*{{{*/
- // ---------------------------------------------------------------------
- /* This helper function attempts a connection to a single address. */
- static bool DoConnect(struct addrinfo *Addr,string Host,
- unsigned long TimeOut,int &Fd,pkgAcqMethod *Owner)
- {
- // Show a status indicator
- char Name[NI_MAXHOST];
- Name[0] = 0;
- getnameinfo(Addr->ai_addr,Addr->ai_addrlen,
- Name,sizeof(Name),0,0,NI_NUMERICHOST);
- Owner->Status("Connecting to %s (%s)",Host.c_str(),Name);
-
- // Get a socket
- if ((Fd = socket(Addr->ai_family,Addr->ai_socktype,
- Addr->ai_protocol)) < 0)
- return _error->Errno("socket","Could not create a socket");
-
- SetNonBlock(Fd,true);
- if (connect(Fd,Addr->ai_addr,Addr->ai_addrlen) < 0 &&
- errno != EINPROGRESS)
- return _error->Errno("connect","Cannot initiate the connection "
- "to %s (%s).",Host.c_str(),Name);
-
- /* This implements a timeout for connect by opening the connection
- nonblocking */
- if (WaitFd(Fd,true,TimeOut) == false)
- return _error->Error("Could not connect to %s (%s), "
- "connection timed out",Host.c_str(),Name);
-
- // Check the socket for an error condition
- unsigned int Err;
- unsigned int Len = sizeof(Err);
- if (getsockopt(Fd,SOL_SOCKET,SO_ERROR,&Err,&Len) != 0)
- return _error->Errno("getsockopt","Failed");
-
- if (Err != 0)
- return _error->Error("Could not connect to %s (%s).",Host.c_str(),Name);
- return true;
- }
- /*}}}*/
- // Connect - Connect to a server /*{{{*/
- // ---------------------------------------------------------------------
- /* Performs a connection to the server */
- bool Connect(string Host,int Port,const char *Service,int DefPort,int &Fd,
- unsigned long TimeOut,pkgAcqMethod *Owner)
- {
- if (_error->PendingError() == true)
- return false;
-
- /* We used a cached address record.. Yes this is against the spec but
- the way we have setup our rotating dns suggests that this is more
- sensible */
- if (LastHost != Host || LastPort != Port)
- {
- Owner->Status("Connecting to %s",Host.c_str());
- // Lookup the host
- char S[300];
- if (Port != 0)
- snprintf(S,sizeof(S),"%u",Port);
- else
- snprintf(S,sizeof(S),"%s",Service);
- // Free the old address structure
- if (LastHostAddr != 0)
- {
- freeaddrinfo(LastHostAddr);
- LastHostAddr = 0;
- }
-
- // We only understand SOCK_STREAM sockets.
- struct addrinfo Hints;
- memset(&Hints,0,sizeof(Hints));
- Hints.ai_socktype = SOCK_STREAM;
- Hints.ai_protocol = IPPROTO_TCP; // Right?
-
- // Resolve both the host and service simultaneously
- while (1)
- {
- int Res;
- if ((Res = getaddrinfo(Host.c_str(),S,&Hints,&LastHostAddr)) != 0 ||
- LastHostAddr == 0)
- {
- if (Res == EAI_NONAME || Res == EAI_SERVICE)
- {
- if (DefPort != 0)
- {
- snprintf(S,sizeof(S),"%u",DefPort);
- DefPort = 0;
- continue;
- }
- return _error->Error("Could not resolve '%s'",Host.c_str());
- }
-
- return _error->Error("Something wicked happend resolving '%s/%s'",
- Host.c_str(),S);
- }
- break;
- }
-
- if (LastHostAddr->ai_family == AF_UNIX)
- return _error->Error("getaddrinfo returned a unix domain socket\n");
-
- LastHost = Host;
- LastPort = Port;
- LastUsed = 0;
- }
- // Get the printable IP address
- struct addrinfo *CurHost = LastHostAddr;
- if (LastUsed != 0)
- CurHost = LastUsed;
-
- while (CurHost != 0)
- {
- if (DoConnect(CurHost,Host,TimeOut,Fd,Owner) == true)
- {
- LastUsed = CurHost;
- return true;
- }
- close(Fd);
- Fd = -1;
-
- CurHost = CurHost->ai_next;
- LastUsed = 0;
- if (CurHost != 0)
- _error->Discard();
- }
-
- if (_error->PendingError() == true)
- return false;
- return _error->Error("Unable to connect to '%s'",Host.c_str());
- }
- /*}}}*/
|