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