connect.cc 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. // -*- mode: cpp; mode: fold -*-
  2. // Description /*{{{*/
  3. // $Id: connect.cc,v 1.6 2000/05/28 04:34:44 jgg Exp $
  4. /* ######################################################################
  5. Connect - Replacement connect call
  6. ##################################################################### */
  7. /*}}}*/
  8. // Include Files /*{{{*/
  9. #include "connect.h"
  10. #include <apt-pkg/error.h>
  11. #include <apt-pkg/fileutl.h>
  12. #include <stdio.h>
  13. #include <errno.h>
  14. #include <unistd.h>
  15. // Internet stuff
  16. #include <netinet/in.h>
  17. #include <sys/socket.h>
  18. #include <arpa/inet.h>
  19. #include <netdb.h>
  20. #include "rfc2553emu.h"
  21. /*}}}*/
  22. static string LastHost;
  23. static int LastPort = 0;
  24. static struct addrinfo *LastHostAddr = 0;
  25. static struct addrinfo *LastUsed = 0;
  26. // DoConnect - Attempt a connect operation /*{{{*/
  27. // ---------------------------------------------------------------------
  28. /* This helper function attempts a connection to a single address. */
  29. static bool DoConnect(struct addrinfo *Addr,string Host,
  30. unsigned long TimeOut,int &Fd,pkgAcqMethod *Owner)
  31. {
  32. // Show a status indicator
  33. char Name[NI_MAXHOST];
  34. char Service[NI_MAXSERV];
  35. Name[0] = 0;
  36. Service[0] = 0;
  37. getnameinfo(Addr->ai_addr,Addr->ai_addrlen,
  38. Name,sizeof(Name),Service,sizeof(Service),
  39. NI_NUMERICHOST|NI_NUMERICSERV);
  40. Owner->Status("Connecting to %s (%s)",Host.c_str(),Name);
  41. // Get a socket
  42. if ((Fd = socket(Addr->ai_family,Addr->ai_socktype,
  43. Addr->ai_protocol)) < 0)
  44. return _error->Errno("socket","Could not create a socket");
  45. SetNonBlock(Fd,true);
  46. if (connect(Fd,Addr->ai_addr,Addr->ai_addrlen) < 0 &&
  47. errno != EINPROGRESS)
  48. return _error->Errno("connect","Cannot initiate the connection "
  49. "to %s:%s (%s).",Host.c_str(),Service,Name);
  50. /* This implements a timeout for connect by opening the connection
  51. nonblocking */
  52. if (WaitFd(Fd,true,TimeOut) == false)
  53. return _error->Error("Could not connect to %s:%s (%s), "
  54. "connection timed out",Host.c_str(),Service,Name);
  55. // Check the socket for an error condition
  56. unsigned int Err;
  57. unsigned int Len = sizeof(Err);
  58. if (getsockopt(Fd,SOL_SOCKET,SO_ERROR,&Err,&Len) != 0)
  59. return _error->Errno("getsockopt","Failed");
  60. if (Err != 0)
  61. {
  62. errno = Err;
  63. return _error->Errno("connect","Could not connect to %s:%s (%s).",Host.c_str(),
  64. Service,Name);
  65. }
  66. return true;
  67. }
  68. /*}}}*/
  69. // Connect - Connect to a server /*{{{*/
  70. // ---------------------------------------------------------------------
  71. /* Performs a connection to the server */
  72. bool Connect(string Host,int Port,const char *Service,int DefPort,int &Fd,
  73. unsigned long TimeOut,pkgAcqMethod *Owner)
  74. {
  75. if (_error->PendingError() == true)
  76. return false;
  77. // Convert the port name/number
  78. char ServStr[300];
  79. if (Port != 0)
  80. snprintf(ServStr,sizeof(ServStr),"%u",Port);
  81. else
  82. snprintf(ServStr,sizeof(ServStr),"%s",Service);
  83. /* We used a cached address record.. Yes this is against the spec but
  84. the way we have setup our rotating dns suggests that this is more
  85. sensible */
  86. if (LastHost != Host || LastPort != Port)
  87. {
  88. Owner->Status("Connecting to %s",Host.c_str());
  89. // Free the old address structure
  90. if (LastHostAddr != 0)
  91. {
  92. freeaddrinfo(LastHostAddr);
  93. LastHostAddr = 0;
  94. LastUsed = 0;
  95. }
  96. // We only understand SOCK_STREAM sockets.
  97. struct addrinfo Hints;
  98. memset(&Hints,0,sizeof(Hints));
  99. Hints.ai_socktype = SOCK_STREAM;
  100. Hints.ai_protocol = 0;
  101. // Resolve both the host and service simultaneously
  102. while (1)
  103. {
  104. int Res;
  105. if ((Res = getaddrinfo(Host.c_str(),ServStr,&Hints,&LastHostAddr)) != 0 ||
  106. LastHostAddr == 0)
  107. {
  108. if (Res == EAI_NONAME || Res == EAI_SERVICE)
  109. {
  110. if (DefPort != 0)
  111. {
  112. snprintf(ServStr,sizeof(ServStr),"%u",DefPort);
  113. DefPort = 0;
  114. continue;
  115. }
  116. return _error->Error("Could not resolve '%s'",Host.c_str());
  117. }
  118. return _error->Error("Something wicked happend resolving '%s:%s'",
  119. Host.c_str(),ServStr);
  120. }
  121. break;
  122. }
  123. LastHost = Host;
  124. LastPort = Port;
  125. }
  126. // When we have an IP rotation stay with the last IP.
  127. struct addrinfo *CurHost = LastHostAddr;
  128. if (LastUsed != 0)
  129. CurHost = LastUsed;
  130. while (CurHost != 0)
  131. {
  132. if (DoConnect(CurHost,Host,TimeOut,Fd,Owner) == true)
  133. {
  134. LastUsed = CurHost;
  135. return true;
  136. }
  137. close(Fd);
  138. Fd = -1;
  139. // Ignore UNIX domain sockets
  140. do
  141. {
  142. CurHost = CurHost->ai_next;
  143. }
  144. while (CurHost != 0 && CurHost->ai_family == AF_UNIX);
  145. LastUsed = 0;
  146. if (CurHost != 0)
  147. _error->Discard();
  148. }
  149. if (_error->PendingError() == true)
  150. return false;
  151. return _error->Error("Unable to connect to %s:",Host.c_str(),ServStr);
  152. }
  153. /*}}}*/