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