1 // -*- mode: cpp; mode: fold -*-
3 /* ######################################################################
7 ##################################################################### */
13 #include <netinet/in.h>
14 #include <arpa/nameser.h>
21 #include <apt-pkg/configuration.h>
22 #include <apt-pkg/error.h>
23 #include <apt-pkg/strutl.h>
29 bool SrvRec::operator==(SrvRec
const &other
) const
31 return (std::tie(target
, priority
, weight
, port
) ==
32 std::tie(other
.target
, other
.priority
, other
.weight
, other
.port
));
35 bool GetSrvRecords(std::string host
, int port
, std::vector
<SrvRec
> &Result
)
39 struct servent s_ent_buf
;
40 struct servent
*s_ent
= nullptr;
41 std::vector
<char> buf(1024);
43 res
= getservbyport_r(htons(port
), "tcp", &s_ent_buf
, buf
.data(), buf
.size(), &s_ent
);
44 if (res
!= 0 || s_ent
== nullptr)
47 strprintf(target
, "_%s._tcp.%s", s_ent
->s_name
, host
.c_str());
48 return GetSrvRecords(target
, Result
);
51 bool GetSrvRecords(std::string name
, std::vector
<SrvRec
> &Result
)
53 unsigned char answer
[NS_PACKETSZ
];
54 int answer_len
, compressed_name_len
;
58 return _error
->Errno("res_init", "Failed to init resolver");
60 answer_len
= res_query(name
.c_str(), C_IN
, T_SRV
, answer
, sizeof(answer
));
63 if (answer_len
< (int)sizeof(HEADER
))
64 return _error
->Warning("Not enough data from res_query (%i)", answer_len
);
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
);
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
);
79 // pt points to the first answer record, go over all of them now
80 unsigned char *pt
= answer
+sizeof(HEADER
)+compressed_name_len
+NS_QFIXEDSZ
;
81 while ((int)Result
.size() < answer_count
&& pt
< answer
+answer_len
)
83 u_int16_t type
, klass
, priority
, weight
, port
, dlen
;
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",
90 pt
+= compressed_name_len
;
91 if (((answer
+answer_len
) - pt
) < 16)
92 return _error
->Warning("packet too short");
94 // extract the data out of the result buffer
95 #define extract_u16(target, p) target = *p++ << 8; target |= *p++;
97 extract_u16(type
, pt
);
99 return _error
->Warning("Unexpected type excepted %x != %x",
101 extract_u16(klass
, pt
);
103 return _error
->Warning("Unexpected class excepted %x != %x",
106 extract_u16(dlen
, pt
);
107 extract_u16(priority
, pt
);
108 extract_u16(weight
, pt
);
109 extract_u16(port
, pt
);
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
;
118 // add it to our class
119 Result
.emplace_back(buf
, priority
, weight
, port
);
122 // implement load balancing as specified in RFC-2782
124 // sort them by priority
125 std::stable_sort(Result
.begin(), Result
.end());
127 for(std::vector
<SrvRec
>::iterator I
= Result
.begin();
128 I
!= Result
.end(); ++I
)
130 if (_config
->FindB("Debug::Acquire::SrvRecs", false) == true)
132 std::cerr
<< "SrvRecs: got " << I
->target
133 << " prio: " << I
->priority
134 << " weight: " << I
->weight
142 SrvRec
PopFromSrvRecs(std::vector
<SrvRec
> &Recs
)
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
149 #if 0 // all code below is only needed for the weight adjusted selection
150 // assign random number ranges
152 int prev_priority
= 0;
153 for(std::vector
<SrvRec
>::iterator I
= Result
.begin();
154 I
!= Result
.end(); ++I
)
156 if(prev_priority
!= I
->priority
)
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
;
163 if (_config
->FindB("Debug::Acquire::SrvRecs", false) == true)
164 std::cerr
<< "SrvRecs: got " << I
->target
165 << " prio: " << I
->priority
166 << " weight: " << I
->weight
170 // go over the code in reverse order and note the max random range
173 for(std::vector
<SrvRec
>::iterator I
= Result
.end();
174 I
!= Result
.begin(); --I
)
176 if(prev_priority
!= I
->priority
)
177 max
= I
->random_number_range_end
;
178 I
->random_number_range_max
= max
;
182 // shuffle in a very simplistic way for now (equal weights)
183 std::vector
<SrvRec
>::iterator I
= Recs
.begin();
184 std::vector
<SrvRec
>::iterator
const J
= std::find_if(Recs
.begin(), Recs
.end(),
185 [&I
](SrvRec
const &J
) { return I
->priority
!= J
.priority
; });
187 // clock seems random enough.
188 I
+= std::max(static_cast<clock_t>(0), clock()) % std::distance(I
, J
);
189 SrvRec
const selected
= std::move(*I
);
192 if (_config
->FindB("Debug::Acquire::SrvRecs", false) == true)
193 std::cerr
<< "PopFromSrvRecs: selecting " << selected
.target
<< std::endl
;