apt  2.2.4
About: Apt (Advanced Package Tool) is a management system for software packages (Debian/Ubuntu). Release series 2.2.
  Fossies Dox: apt-2.2.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

srvrec.cc
Go to the documentation of this file.
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 /* ######################################################################
4 
5  SRV record support
6 
7  ##################################################################### */
8  /*}}}*/
9 #include <config.h>
10 
11 #include <netdb.h>
12 
13 #include <arpa/nameser.h>
14 #include <arpa/inet.h>
15 #include <netinet/in.h>
16 #include <resolv.h>
17 #include <time.h>
18 
19 #include <algorithm>
20 #include <memory>
21 #include <tuple>
22 
23 #include <apt-pkg/configuration.h>
24 #include <apt-pkg/error.h>
25 #include <apt-pkg/strutl.h>
26 
27 #include "srvrec.h"
28 
29 bool SrvRec::operator==(SrvRec const &other) const
30 {
31  return (std::tie(target, priority, weight, port) ==
32  std::tie(other.target, other.priority, other.weight, other.port));
33 }
34 
35 bool GetSrvRecords(std::string host, int port, std::vector<SrvRec> &Result)
36 {
37  // try SRV only for hostnames, not for IP addresses
38  {
39  struct in_addr addr4;
40  struct in6_addr addr6;
41  if (inet_pton(AF_INET, host.c_str(), &addr4) == 1 ||
42  inet_pton(AF_INET6, host.c_str(), &addr6) == 1)
43  return true;
44  }
45 
46  std::string target;
47  int res;
48  struct servent s_ent_buf;
49  struct servent *s_ent = nullptr;
50  std::vector<char> buf(1024);
51 
52  res = getservbyport_r(htons(port), "tcp", &s_ent_buf, buf.data(), buf.size(), &s_ent);
53  if (res != 0 || s_ent == nullptr)
54  return false;
55 
56  strprintf(target, "_%s._tcp.%s", s_ent->s_name, host.c_str());
57  return GetSrvRecords(target, Result);
58 }
59 
60 bool GetSrvRecords(std::string name, std::vector<SrvRec> &Result)
61 {
62  unsigned char answer[PACKETSZ];
63  int answer_len, compressed_name_len;
64  int answer_count;
65  struct __res_state res;
66 
67  if (res_ninit(&res) != 0)
68  return _error->Errno("res_init", "Failed to init resolver");
69 
70  // Close on return
71  std::shared_ptr<void> guard(&res, res_nclose);
72 
73  answer_len = res_nquery(&res, name.c_str(), C_IN, T_SRV, answer, sizeof(answer));
74  if (answer_len == -1)
75  return false;
76  if (answer_len < (int)sizeof(HEADER))
77  return _error->Warning("Not enough data from res_query (%i)", answer_len);
78 
79  // check the header
80  HEADER *header = (HEADER*)answer;
81  if (header->rcode != NOERROR)
82  return _error->Warning("res_query returned rcode %i", header->rcode);
83  answer_count = ntohs(header->ancount);
84  if (answer_count <= 0)
85  return _error->Warning("res_query returned no answers (%i) ", answer_count);
86 
87  // skip the header
88  compressed_name_len = dn_skipname(answer+sizeof(HEADER), answer+answer_len);
89  if(compressed_name_len < 0)
90  return _error->Warning("dn_skipname failed %i", compressed_name_len);
91 
92  // pt points to the first answer record, go over all of them now
93  unsigned char *pt = answer+sizeof(HEADER)+compressed_name_len+QFIXEDSZ;
94  while ((int)Result.size() < answer_count && pt < answer+answer_len)
95  {
96  u_int16_t type, klass, priority, weight, port, dlen;
97  char buf[MAXDNAME];
98 
99  compressed_name_len = dn_skipname(pt, answer+answer_len);
100  if (compressed_name_len < 0)
101  return _error->Warning("dn_skipname failed (2): %i",
102  compressed_name_len);
103  pt += compressed_name_len;
104  if (((answer+answer_len) - pt) < 16)
105  return _error->Warning("packet too short");
106 
107  // extract the data out of the result buffer
108  #define extract_u16(target, p) target = *p++ << 8; target |= *p++;
109 
110  extract_u16(type, pt);
111  if(type != T_SRV)
112  return _error->Warning("Unexpected type excepted %x != %x",
113  T_SRV, type);
114  extract_u16(klass, pt);
115  if(klass != C_IN)
116  return _error->Warning("Unexpected class excepted %x != %x",
117  C_IN, klass);
118  pt += 4; // ttl
119  extract_u16(dlen, pt);
120  extract_u16(priority, pt);
121  extract_u16(weight, pt);
122  extract_u16(port, pt);
123 
124  #undef extract_u16
125 
126  compressed_name_len = dn_expand(answer, answer+answer_len, pt, buf, sizeof(buf));
127  if(compressed_name_len < 0)
128  return _error->Warning("dn_expand failed %i", compressed_name_len);
129  pt += compressed_name_len;
130 
131  // add it to our class
132  Result.emplace_back(buf, priority, weight, port);
133  }
134 
135  // implement load balancing as specified in RFC-2782
136 
137  // sort them by priority
138  std::stable_sort(Result.begin(), Result.end());
139 
140  if (_config->FindB("Debug::Acquire::SrvRecs", false))
141  for(auto const &R : Result)
142  std::cerr << "SrvRecs: got " << R.target
143  << " prio: " << R.priority
144  << " weight: " << R.weight
145  << '\n';
146 
147  return true;
148 }
149 
150 SrvRec PopFromSrvRecs(std::vector<SrvRec> &Recs)
151 {
152  // FIXME: instead of the simplistic shuffle below use the algorithm
153  // described in rfc2782 (with weights)
154  // and figure out how the weights need to be adjusted if
155  // a host refuses connections
156 
157 #if 0 // all code below is only needed for the weight adjusted selection
158  // assign random number ranges
159  int prev_weight = 0;
160  int prev_priority = 0;
161  for(std::vector<SrvRec>::iterator I = Result.begin();
162  I != Result.end(); ++I)
163  {
164  if(prev_priority != I->priority)
165  prev_weight = 0;
166  I->random_number_range_start = prev_weight;
167  I->random_number_range_end = prev_weight + I->weight;
168  prev_weight = I->random_number_range_end;
169  prev_priority = I->priority;
170 
171  if (_config->FindB("Debug::Acquire::SrvRecs", false) == true)
172  std::cerr << "SrvRecs: got " << I->target
173  << " prio: " << I->priority
174  << " weight: " << I->weight
175  << std::endl;
176  }
177 
178  // go over the code in reverse order and note the max random range
179  int max = 0;
180  prev_priority = 0;
181  for(std::vector<SrvRec>::iterator I = Result.end();
182  I != Result.begin(); --I)
183  {
184  if(prev_priority != I->priority)
185  max = I->random_number_range_end;
186  I->random_number_range_max = max;
187  }
188 #endif
189 
190  // shuffle in a very simplistic way for now (equal weights)
191  std::vector<SrvRec>::iterator I = Recs.begin();
192  std::vector<SrvRec>::iterator const J = std::find_if(Recs.begin(), Recs.end(),
193  [&I](SrvRec const &J) { return I->priority != J.priority; });
194 
195  // clock seems random enough.
196  I += std::max(static_cast<clock_t>(0), clock()) % std::distance(I, J);
197  SrvRec const selected = std::move(*I);
198  Recs.erase(I);
199 
200  if (_config->FindB("Debug::Acquire::SrvRecs", false) == true)
201  std::cerr << "PopFromSrvRecs: selecting " << selected.target << std::endl;
202 
203  return selected;
204 }
strprintf(m, msg, repo.c_str())
static bool std::string const metaIndex const *const pkgAcqMetaClearSig *const pkgAcquire::Item *const I
bool FindB(const char *Name, bool const &Default=false) const
Definition: srvrec.h:19
u_int16_t weight
Definition: srvrec.h:23
u_int16_t priority
Definition: srvrec.h:22
u_int16_t port
Definition: srvrec.h:24
std::string target
Definition: srvrec.h:21
bool operator==(SrvRec const &other) const
Definition: srvrec.cc:29
Configuration * _config
#define extract_u16(target, p)
bool GetSrvRecords(std::string host, int port, std::vector< SrvRec > &Result)
Get SRV records for query string like: _http._tcp.example.com.
Definition: srvrec.cc:35
SrvRec PopFromSrvRecs(std::vector< SrvRec > &Recs)
Pop a single SRV record from the vector of SrvRec taking priority and weight into account.
Definition: srvrec.cc:150