connect.cc 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. // -*- mode: cpp; mode: fold -*-
  2. // Description /*{{{*/
  3. // $Id: connect.cc,v 1.4 1999/11/19 05:01:54 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. Name[0] = 0;
  35. getnameinfo(Addr->ai_addr,Addr->ai_addrlen,
  36. Name,sizeof(Name),0,0,NI_NUMERICHOST);
  37. Owner->Status("Connecting to %s (%s)",Host.c_str(),Name);
  38. // Get a socket
  39. if ((Fd = socket(Addr->ai_family,Addr->ai_socktype,
  40. Addr->ai_protocol)) < 0)
  41. return _error->Errno("socket","Could not create a socket");
  42. SetNonBlock(Fd,true);
  43. if (connect(Fd,Addr->ai_addr,Addr->ai_addrlen) < 0 &&
  44. errno != EINPROGRESS)
  45. return _error->Errno("connect","Cannot initiate the connection "
  46. "to %s (%s).",Host.c_str(),Name);
  47. /* This implements a timeout for connect by opening the connection
  48. nonblocking */
  49. if (WaitFd(Fd,true,TimeOut) == false)
  50. return _error->Error("Could not connect to %s (%s), "
  51. "connection timed out",Host.c_str(),Name);
  52. // Check the socket for an error condition
  53. unsigned int Err;
  54. unsigned int Len = sizeof(Err);
  55. if (getsockopt(Fd,SOL_SOCKET,SO_ERROR,&Err,&Len) != 0)
  56. return _error->Errno("getsockopt","Failed");
  57. if (Err != 0)
  58. return _error->Error("Could not connect to %s (%s).",Host.c_str(),Name);
  59. return true;
  60. }
  61. /*}}}*/
  62. // Connect - Connect to a server /*{{{*/
  63. // ---------------------------------------------------------------------
  64. /* Performs a connection to the server */
  65. bool Connect(string Host,int Port,const char *Service,int DefPort,int &Fd,
  66. unsigned long TimeOut,pkgAcqMethod *Owner)
  67. {
  68. if (_error->PendingError() == true)
  69. return false;
  70. /* We used a cached address record.. Yes this is against the spec but
  71. the way we have setup our rotating dns suggests that this is more
  72. sensible */
  73. if (LastHost != Host || LastPort != Port)
  74. {
  75. Owner->Status("Connecting to %s",Host.c_str());
  76. // Lookup the host
  77. char S[300];
  78. if (Port != 0)
  79. snprintf(S,sizeof(S),"%u",Port);
  80. else
  81. snprintf(S,sizeof(S),"%s",Service);
  82. // Free the old address structure
  83. if (LastHostAddr != 0)
  84. {
  85. freeaddrinfo(LastHostAddr);
  86. LastHostAddr = 0;
  87. }
  88. // We only understand SOCK_STREAM sockets.
  89. struct addrinfo Hints;
  90. memset(&Hints,0,sizeof(Hints));
  91. Hints.ai_socktype = SOCK_STREAM;
  92. Hints.ai_protocol = IPPROTO_TCP; // Right?
  93. // Resolve both the host and service simultaneously
  94. while (1)
  95. {
  96. int Res;
  97. if ((Res = getaddrinfo(Host.c_str(),S,&Hints,&LastHostAddr)) != 0 ||
  98. LastHostAddr == 0)
  99. {
  100. if (Res == EAI_NONAME || Res == EAI_SERVICE)
  101. {
  102. if (DefPort != 0)
  103. {
  104. snprintf(S,sizeof(S),"%u",DefPort);
  105. DefPort = 0;
  106. continue;
  107. }
  108. return _error->Error("Could not resolve '%s'",Host.c_str());
  109. }
  110. return _error->Error("Something wicked happend resolving '%s/%s'",
  111. Host.c_str(),S);
  112. }
  113. break;
  114. }
  115. if (LastHostAddr->ai_family == AF_UNIX)
  116. return _error->Error("getaddrinfo returned a unix domain socket\n");
  117. LastHost = Host;
  118. LastPort = Port;
  119. LastUsed = 0;
  120. }
  121. // Get the printable IP address
  122. struct addrinfo *CurHost = LastHostAddr;
  123. if (LastUsed != 0)
  124. CurHost = LastUsed;
  125. while (CurHost != 0)
  126. {
  127. if (DoConnect(CurHost,Host,TimeOut,Fd,Owner) == true)
  128. {
  129. LastUsed = CurHost;
  130. return true;
  131. }
  132. close(Fd);
  133. Fd = -1;
  134. CurHost = CurHost->ai_next;
  135. LastUsed = 0;
  136. if (CurHost != 0)
  137. _error->Discard();
  138. }
  139. return false;
  140. }
  141. /*}}}*/