srvrec.cc 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  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 <resolv.h>
  12. #include <algorithm>
  13. #include <apt-pkg/strutl.h>
  14. #include <apt-pkg/error.h>
  15. #include "srvrec.h"
  16. bool GetSrvRecords(std::string host, int port, std::vector<SrvRec> &Result)
  17. {
  18. std::string target;
  19. struct servent *s_ent = getservbyport(htons(port), "tcp");
  20. if (s_ent == NULL)
  21. return false;
  22. strprintf(target, "_%s._tcp.%s", s_ent->s_name, host.c_str());
  23. return GetSrvRecords(target, Result);
  24. }
  25. bool GetSrvRecords(std::string name, std::vector<SrvRec> &Result)
  26. {
  27. unsigned char answer[PACKETSZ];
  28. int answer_len, compressed_name_len;
  29. int answer_count;
  30. if (res_init() != 0)
  31. return _error->Errno("res_init", "Failed to init resolver");
  32. answer_len = res_query(name.c_str(), C_IN, T_SRV, answer, sizeof(answer));
  33. if (answer_len == -1)
  34. return false;
  35. if (answer_len < (int)sizeof(HEADER))
  36. return _error->Warning("Not enough data from res_query (%i)", answer_len);
  37. // check the header
  38. HEADER *header = (HEADER*)answer;
  39. if (header->rcode != NOERROR)
  40. return _error->Warning("res_query returned rcode %i", header->rcode);
  41. answer_count = ntohs(header->ancount);
  42. if (answer_count <= 0)
  43. return _error->Warning("res_query returned no answers (%i) ", answer_count);
  44. // skip the header
  45. compressed_name_len = dn_skipname(answer+sizeof(HEADER), answer+answer_len);
  46. if(compressed_name_len < 0)
  47. return _error->Warning("dn_skipname failed %i", compressed_name_len);
  48. // pt points to the first answer record, go over all of them now
  49. unsigned char *pt = answer+sizeof(HEADER)+compressed_name_len+QFIXEDSZ;
  50. while ((int)Result.size() < answer_count && pt < answer+answer_len)
  51. {
  52. SrvRec rec;
  53. u_int16_t type, klass, priority, weight, port, dlen;
  54. char buf[MAXDNAME];
  55. compressed_name_len = dn_skipname(pt, answer+answer_len);
  56. if (compressed_name_len < 0)
  57. return _error->Warning("dn_skipname failed (2): %i",
  58. compressed_name_len);
  59. pt += compressed_name_len;
  60. if (((answer+answer_len) - pt) < 16)
  61. return _error->Warning("packet too short");
  62. // extract the data out of the result buffer
  63. #define extract_u16(target, p) target = *p++ << 8; target |= *p++;
  64. extract_u16(type, pt);
  65. if(type != T_SRV)
  66. return _error->Warning("Unexpected type excepted %x != %x",
  67. T_SRV, type);
  68. extract_u16(klass, pt);
  69. if(klass != C_IN)
  70. return _error->Warning("Unexpected class excepted %x != %x",
  71. C_IN, klass);
  72. pt += 4; // ttl
  73. extract_u16(dlen, pt);
  74. extract_u16(priority, pt);
  75. extract_u16(weight, pt);
  76. extract_u16(port, pt);
  77. #undef extract_u16
  78. compressed_name_len = dn_expand(answer, answer+answer_len, pt, buf, sizeof(buf));
  79. if(compressed_name_len < 0)
  80. return _error->Warning("dn_expand failed %i", compressed_name_len);
  81. pt += compressed_name_len;
  82. // add it to our class
  83. rec.priority = priority;
  84. rec.weight = weight;
  85. rec.port = port;
  86. rec.target = buf;
  87. Result.push_back(rec);
  88. }
  89. // implement load balancing as specified in RFC-2782
  90. // sort them by priority
  91. std::stable_sort(Result.begin(), Result.end());
  92. // assign random number ranges
  93. int prev_weight = 0;
  94. int prev_priority = 0;
  95. for(std::vector<SrvRec>::iterator I = Result.begin();
  96. I != Result.end(); ++I)
  97. {
  98. if(prev_priority != I->priority)
  99. prev_weight = 0;
  100. I->random_number_range_start = prev_weight;
  101. I->random_number_range_end = prev_weight + I->weight;
  102. prev_weight = I->random_number_range_end;
  103. prev_priority = I->priority;
  104. }
  105. // go over the code in reverse order and note the max random range
  106. int max = 0;
  107. prev_priority = 0;
  108. for(std::vector<SrvRec>::iterator I = Result.end();
  109. I != Result.begin(); --I)
  110. {
  111. if(prev_priority != I->priority)
  112. max = I->random_number_range_end;
  113. I->random_number_range_max = max;
  114. }
  115. // FIXME: now shuffle
  116. return true;
  117. }