srvrec.cc 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // -*- mode: cpp; mode: fold -*-
  2. // Description /*{{{*/
  3. /* ######################################################################
  4. SRV record support
  5. ##################################################################### */
  6. /*}}}*/
  7. #include <config.h>
  8. #include <netdb.h>
  9. #include <netinet/in.h>
  10. #include <arpa/nameser.h>
  11. #include <apt-pkg/nameser_compat.h>
  12. #include <resolv.h>
  13. #include <time.h>
  14. #include <algorithm>
  15. #include <tuple>
  16. #include <apt-pkg/configuration.h>
  17. #include <apt-pkg/error.h>
  18. #include <apt-pkg/strutl.h>
  19. #include "srvrec.h"
  20. bool SrvRec::operator==(SrvRec const &other) const
  21. {
  22. return (std::tie(target, priority, weight, port) ==
  23. std::tie(other.target, other.priority, other.weight, other.port));
  24. }
  25. bool GetSrvRecords(std::string host, int port, std::vector<SrvRec> &Result)
  26. {
  27. std::string target;
  28. int res;
  29. struct servent s_ent_buf;
  30. struct servent *s_ent = nullptr;
  31. std::vector<char> buf(1024);
  32. res = getservbyport_r(htons(port), "tcp", &s_ent_buf, buf.data(), buf.size(), &s_ent);
  33. if (res != 0 || s_ent == nullptr)
  34. return false;
  35. strprintf(target, "_%s._tcp.%s", s_ent->s_name, host.c_str());
  36. return GetSrvRecords(target, Result);
  37. }
  38. bool GetSrvRecords(std::string name, std::vector<SrvRec> &Result)
  39. {
  40. unsigned char answer[NS_PACKETSZ];
  41. int answer_len, compressed_name_len;
  42. int answer_count;
  43. if (res_init() != 0)
  44. return _error->Errno("res_init", "Failed to init resolver");
  45. answer_len = res_query(name.c_str(), C_IN, T_SRV, answer, sizeof(answer));
  46. if (answer_len == -1)
  47. return false;
  48. if (answer_len < (int)sizeof(HEADER))
  49. return _error->Warning("Not enough data from res_query (%i)", answer_len);
  50. // check the header
  51. HEADER *header = (HEADER*)answer;
  52. if (header->rcode != NOERROR)
  53. return _error->Warning("res_query returned rcode %i", header->rcode);
  54. answer_count = ntohs(header->ancount);
  55. if (answer_count <= 0)
  56. return _error->Warning("res_query returned no answers (%i) ", answer_count);
  57. // skip the header
  58. compressed_name_len = dn_skipname(answer+sizeof(HEADER), answer+answer_len);
  59. if(compressed_name_len < 0)
  60. return _error->Warning("dn_skipname failed %i", compressed_name_len);
  61. // pt points to the first answer record, go over all of them now
  62. unsigned char *pt = answer+sizeof(HEADER)+compressed_name_len+NS_QFIXEDSZ;
  63. while ((int)Result.size() < answer_count && pt < answer+answer_len)
  64. {
  65. u_int16_t type, klass, priority, weight, port, dlen;
  66. char buf[MAXDNAME];
  67. compressed_name_len = dn_skipname(pt, answer+answer_len);
  68. if (compressed_name_len < 0)
  69. return _error->Warning("dn_skipname failed (2): %i",
  70. compressed_name_len);
  71. pt += compressed_name_len;
  72. if (((answer+answer_len) - pt) < 16)
  73. return _error->Warning("packet too short");
  74. // extract the data out of the result buffer
  75. #define extract_u16(target, p) target = *p++ << 8; target |= *p++;
  76. extract_u16(type, pt);
  77. if(type != T_SRV)
  78. return _error->Warning("Unexpected type excepted %x != %x",
  79. T_SRV, type);
  80. extract_u16(klass, pt);
  81. if(klass != C_IN)
  82. return _error->Warning("Unexpected class excepted %x != %x",
  83. C_IN, klass);
  84. pt += 4; // ttl
  85. extract_u16(dlen, pt);
  86. extract_u16(priority, pt);
  87. extract_u16(weight, pt);
  88. extract_u16(port, pt);
  89. #undef extract_u16
  90. compressed_name_len = dn_expand(answer, answer+answer_len, pt, buf, sizeof(buf));
  91. if(compressed_name_len < 0)
  92. return _error->Warning("dn_expand failed %i", compressed_name_len);
  93. pt += compressed_name_len;
  94. // add it to our class
  95. Result.emplace_back(buf, priority, weight, port);
  96. }
  97. // implement load balancing as specified in RFC-2782
  98. // sort them by priority
  99. std::stable_sort(Result.begin(), Result.end());
  100. for(std::vector<SrvRec>::iterator I = Result.begin();
  101. I != Result.end(); ++I)
  102. {
  103. if (_config->FindB("Debug::Acquire::SrvRecs", false) == true)
  104. {
  105. std::cerr << "SrvRecs: got " << I->target
  106. << " prio: " << I->priority
  107. << " weight: " << I->weight
  108. << std::endl;
  109. }
  110. }
  111. return true;
  112. }
  113. SrvRec PopFromSrvRecs(std::vector<SrvRec> &Recs)
  114. {
  115. // FIXME: instead of the simplistic shuffle below use the algorithm
  116. // described in rfc2782 (with weights)
  117. // and figure out how the weights need to be adjusted if
  118. // a host refuses connections
  119. #if 0 // all code below is only needed for the weight adjusted selection
  120. // assign random number ranges
  121. int prev_weight = 0;
  122. int prev_priority = 0;
  123. for(std::vector<SrvRec>::iterator I = Result.begin();
  124. I != Result.end(); ++I)
  125. {
  126. if(prev_priority != I->priority)
  127. prev_weight = 0;
  128. I->random_number_range_start = prev_weight;
  129. I->random_number_range_end = prev_weight + I->weight;
  130. prev_weight = I->random_number_range_end;
  131. prev_priority = I->priority;
  132. if (_config->FindB("Debug::Acquire::SrvRecs", false) == true)
  133. std::cerr << "SrvRecs: got " << I->target
  134. << " prio: " << I->priority
  135. << " weight: " << I->weight
  136. << std::endl;
  137. }
  138. // go over the code in reverse order and note the max random range
  139. int max = 0;
  140. prev_priority = 0;
  141. for(std::vector<SrvRec>::iterator I = Result.end();
  142. I != Result.begin(); --I)
  143. {
  144. if(prev_priority != I->priority)
  145. max = I->random_number_range_end;
  146. I->random_number_range_max = max;
  147. }
  148. #endif
  149. // shuffle in a very simplistic way for now (equal weights)
  150. std::vector<SrvRec>::iterator I = Recs.begin();
  151. std::vector<SrvRec>::iterator const J = std::find_if(Recs.begin(), Recs.end(),
  152. [&I](SrvRec const &J) { return I->priority != J.priority; });
  153. // clock seems random enough.
  154. I += std::max(static_cast<clock_t>(0), clock()) % std::distance(I, J);
  155. SrvRec const selected = std::move(*I);
  156. Recs.erase(I);
  157. if (_config->FindB("Debug::Acquire::SrvRecs", false) == true)
  158. std::cerr << "PopFromSrvRecs: selecting " << selected.target << std::endl;
  159. return selected;
  160. }