X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/f106fecc1d7d0250d1d76c2ba837dc0c870c5fff..671a55ba455dcf4e5ce6d86b202761666f54d5c6:/apt-pkg/contrib/srvrec.cc diff --git a/apt-pkg/contrib/srvrec.cc b/apt-pkg/contrib/srvrec.cc index d5d89fc67..be159bad9 100644 --- a/apt-pkg/contrib/srvrec.cc +++ b/apt-pkg/contrib/srvrec.cc @@ -8,13 +8,46 @@ /*}}}*/ #include +#include + #include #include #include +#include + +#include +#include +#include #include +#include + + #include "srvrec.h" + +bool SrvRec::operator==(SrvRec const &other) const +{ + return (std::tie(target, priority, weight, port) == + std::tie(other.target, other.priority, other.weight, other.port)); +} + +bool GetSrvRecords(std::string host, int port, std::vector &Result) +{ + std::string target; + int res; + struct servent s_ent_buf; + struct servent *s_ent = nullptr; + std::vector buf(1024); + + res = getservbyport_r(htons(port), "tcp", &s_ent_buf, buf.data(), buf.size(), &s_ent); + if (res != 0 || s_ent == nullptr) + return false; + + strprintf(target, "_%s._tcp.%s", s_ent->s_name, host.c_str()); + return GetSrvRecords(target, Result); +} + bool GetSrvRecords(std::string name, std::vector &Result) { unsigned char answer[PACKETSZ]; @@ -25,6 +58,8 @@ bool GetSrvRecords(std::string name, std::vector &Result) return _error->Errno("res_init", "Failed to init resolver"); answer_len = res_query(name.c_str(), C_IN, T_SRV, answer, sizeof(answer)); + if (answer_len == -1) + return false; if (answer_len < (int)sizeof(HEADER)) return _error->Warning("Not enough data from res_query (%i)", answer_len); @@ -45,10 +80,9 @@ bool GetSrvRecords(std::string name, std::vector &Result) unsigned char *pt = answer+sizeof(HEADER)+compressed_name_len+QFIXEDSZ; while ((int)Result.size() < answer_count && pt < answer+answer_len) { - SrvRec rec; u_int16_t type, klass, priority, weight, port, dlen; char buf[MAXDNAME]; - + compressed_name_len = dn_skipname(pt, answer+answer_len); if (compressed_name_len < 0) return _error->Warning("dn_skipname failed (2): %i", @@ -58,15 +92,15 @@ bool GetSrvRecords(std::string name, std::vector &Result) return _error->Warning("packet too short"); // extract the data out of the result buffer - #define extract_u16(target, p) target = *p++ << 8; target |= *p++; + #define extract_u16(target, p) target = *p++ << 8; target |= *p++; extract_u16(type, pt); if(type != T_SRV) - return _error->Warning("Unexpected type excepted %x != %x", + return _error->Warning("Unexpected type excepted %x != %x", T_SRV, type); extract_u16(klass, pt); if(klass != C_IN) - return _error->Warning("Unexpected class excepted %x != %x", + return _error->Warning("Unexpected class excepted %x != %x", C_IN, klass); pt += 4; // ttl extract_u16(dlen, pt); @@ -82,12 +116,81 @@ bool GetSrvRecords(std::string name, std::vector &Result) pt += compressed_name_len; // add it to our class - rec.priority = priority; - rec.weight = weight; - rec.port = port; - rec.target = buf; - Result.push_back(rec); + Result.emplace_back(buf, priority, weight, port); + } + + // implement load balancing as specified in RFC-2782 + + // sort them by priority + std::stable_sort(Result.begin(), Result.end()); + + for(std::vector::iterator I = Result.begin(); + I != Result.end(); ++I) + { + if (_config->FindB("Debug::Acquire::SrvRecs", false) == true) + { + std::cerr << "SrvRecs: got " << I->target + << " prio: " << I->priority + << " weight: " << I->weight + << std::endl; + } } return true; } + +SrvRec PopFromSrvRecs(std::vector &Recs) +{ + // FIXME: instead of the simplistic shuffle below use the algorithm + // described in rfc2782 (with weights) + // and figure out how the weights need to be adjusted if + // a host refuses connections + +#if 0 // all code below is only needed for the weight adjusted selection + // assign random number ranges + int prev_weight = 0; + int prev_priority = 0; + for(std::vector::iterator I = Result.begin(); + I != Result.end(); ++I) + { + if(prev_priority != I->priority) + prev_weight = 0; + I->random_number_range_start = prev_weight; + I->random_number_range_end = prev_weight + I->weight; + prev_weight = I->random_number_range_end; + prev_priority = I->priority; + + if (_config->FindB("Debug::Acquire::SrvRecs", false) == true) + std::cerr << "SrvRecs: got " << I->target + << " prio: " << I->priority + << " weight: " << I->weight + << std::endl; + } + + // go over the code in reverse order and note the max random range + int max = 0; + prev_priority = 0; + for(std::vector::iterator I = Result.end(); + I != Result.begin(); --I) + { + if(prev_priority != I->priority) + max = I->random_number_range_end; + I->random_number_range_max = max; + } +#endif + + // shuffle in a very simplistic way for now (equal weights) + std::vector::iterator I = Recs.begin(); + std::vector::iterator const J = std::find_if(Recs.begin(), Recs.end(), + [&I](SrvRec const &J) { return I->priority != J.priority; }); + + // clock seems random enough. + I += clock() % std::distance(I, J); + SrvRec const selected = std::move(*I); + Recs.erase(I); + + if (_config->FindB("Debug::Acquire::SrvRecs", false) == true) + std::cerr << "PopFromSrvRecs: selecting " << selected.target << std::endl; + + return selected; +}