diff options
author | Don Kjer <don@lindenlab.com> | 2007-07-02 17:10:30 +0000 |
---|---|---|
committer | Don Kjer <don@lindenlab.com> | 2007-07-02 17:10:30 +0000 |
commit | e5124431b54d4342d4677371fccca5bc7250c079 (patch) | |
tree | 8c9636e78e93cef6ed099d9abd72ec9ccbbf35fe /indra/newview/llsrv.cpp | |
parent | ce5e13630cd8f4174549a3ec4ae8c24eec90bb3d (diff) |
svn merge -r 64079:64548 svn+ssh://svn/svn/linden/branches/maintenance into release
Diffstat (limited to 'indra/newview/llsrv.cpp')
-rw-r--r-- | indra/newview/llsrv.cpp | 232 |
1 files changed, 197 insertions, 35 deletions
diff --git a/indra/newview/llsrv.cpp b/indra/newview/llsrv.cpp index cd05d9c107..7669eb994a 100644 --- a/indra/newview/llsrv.cpp +++ b/indra/newview/llsrv.cpp @@ -53,49 +53,37 @@ vector<LLSRVRecord> LLSRV::query(const string& name) #include <netdb.h> -vector<LLSRVRecord> LLSRV::query(const string& queryName) +#ifdef HOMEGROWN_RESPONSE_PARSER + +// We ought to be using libresolv's ns_initparse and ns_parserr to +// parse the result of our query. However, libresolv isn't packaged +// correctly on Linux (as of BIND 9), so neither of these functions is +// available without statically linking against libresolv. Ugh! This +// fallback function is available if we need to parse the response +// ourselves without relying too much on libresolv. It is NOT THE +// DEFAULT. + +vector<LLSRVRecord> LLSRV::parseResponse(const unsigned char *response, + int resp_len) { - unsigned char response[16384]; vector<LLSRVRecord> recs; - char name[1024]; - int len; - - len = res_query(queryName.c_str(), ns_c_in, ns_t_srv, response, - sizeof(response)); - - if (len == -1) - { - llinfos << "Query failed for " << queryName << llendl; - return recs; - } - else if (len > (int) sizeof(response)) - { - llinfos << "Response too big for " << queryName - << " (capacity " << sizeof(response) - << ", response " << len << ")" << llendl; - return recs; - } - - // We "should" be using libresolv's ns_initparse and ns_parserr to - // parse the result of our query. However, libresolv isn't - // packaged correctly on Linux (as of BIND 9), so neither of these - // functions is available without statically linking against - // libresolv. Ugh! So we parse the response ourselves. const unsigned char *pos = response + sizeof(HEADER); - const unsigned char *end = response + len; + const unsigned char *end = response + resp_len; const HEADER *hdr = (const HEADER *) response; + char name[1024]; // Skip over the query embedded in the response. for (int q = ntohs(hdr->qdcount); q > 0; --q) { - len = dn_expand(response, end, pos, name, sizeof(name)); + int len = dn_expand(response, end, pos, name, sizeof(name)); if (len == -1) { - llinfos << "Could not expand queried name in RR response" << llendl; - return recs; + llinfos << "Could not expand queried name in RR response" + << llendl; + goto bail; } pos += len + NS_QFIXEDSZ; @@ -105,11 +93,12 @@ vector<LLSRVRecord> LLSRV::query(const string& queryName) { static const ns_rr *rr; - len = dn_expand(response, end, pos, name, sizeof(name) - 1); + int len = dn_expand(response, end, pos, name, sizeof(name) - 1); if (len == -1) { - llinfos << "Could not expand response name in RR response" << llendl; - return recs; + llinfos << "Could not expand response name in RR response" + << llendl; + goto bail; } // Skip over the resource name and headers we don't care about. @@ -130,7 +119,7 @@ vector<LLSRVRecord> LLSRV::query(const string& queryName) if (len == -1) { llinfos << "Could not expand name in RR response" << llendl; - return recs; + goto bail; } recs.push_back(LLSRVRecord(prio, weight, name, port)); @@ -138,12 +127,177 @@ vector<LLSRVRecord> LLSRV::query(const string& queryName) // There are likely to be more records in the response, but we // don't care about those, at least for now. +bail: + return reorder(recs); +} - return recs; +#else // HOMEGROWN_RESPONSE_PARSER + +// This version of the response parser is the one to use if libresolv +// is available and behaving itself. + +vector<LLSRVRecord> LLSRV::parseResponse(const unsigned char *response, + int resp_len) +{ + vector<LLSRVRecord> recs; + ns_msg hdr; + + if (ns_initparse(response, resp_len, &hdr)) + { + llinfos << "Could not parse response" << llendl; + goto bail; + } + + for (int i = 0; i < ns_msg_count(hdr, ns_s_an); i++) + { + ns_rr rr; + + if (ns_parserr(&hdr, ns_s_an, i, &rr)) + { + llinfos << "Could not parse RR" << llendl; + goto bail; + } + + if (ns_rr_type(rr) != ns_t_srv) + { + continue; + } + + const unsigned char *pos = ns_rr_rdata(rr); + U16 prio, weight, port; + char name[1024]; + int ret; + + NS_GET16(prio, pos); + NS_GET16(weight, pos); + NS_GET16(port, pos); + + ret = dn_expand(ns_msg_base(hdr), ns_msg_end(hdr), pos, + name, sizeof(name)); + + if (ret == -1) + { + llinfos << "Could not decompress name" << llendl; + goto bail; + } + + recs.push_back(LLSRVRecord(prio, weight, name, port)); + } + +bail: + return reorder(recs); +} + +#endif // HOMEGROWN_RESPONSE_PARSER + +vector<LLSRVRecord> LLSRV::query(const string& queryName) +{ + unsigned char response[16384]; + vector<LLSRVRecord> recs; + int len; + + len = res_query(queryName.c_str(), ns_c_in, ns_t_srv, response, + sizeof(response)); + + if (len == -1) + { + llinfos << "Query failed for " << queryName << llendl; + goto bail; + } + else if (len > (int) sizeof(response)) + { + llinfos << "Response too big for " << queryName + << " (capacity " << sizeof(response) + << ", response " << len << ")" << llendl; + goto bail; + } + + recs = parseResponse(response, len); +bail: + return reorder(recs); } #endif // LL_WINDOWS +// Implement the algorithm specified in RFC 2782 for dealing with RRs +// of differing priorities and weights. +vector<LLSRVRecord> LLSRV::reorder(vector<LLSRVRecord>& recs) +{ + typedef list<const LLSRVRecord *> reclist_t; + typedef map<U16, reclist_t> bucket_t; + vector<LLSRVRecord> newRecs; + bucket_t buckets; + + // Don't rely on the DNS server to shuffle responses. + + random_shuffle(recs.begin(), recs.end()); + + for (vector<LLSRVRecord>::const_iterator iter = recs.begin(); + iter != recs.end(); ++iter) + { + buckets[iter->priority()].push_back(&*iter); + } + + // Priorities take precedence over weights. + + for (bucket_t::iterator iter = buckets.begin(); + iter != buckets.end(); ++iter) + { + reclist_t& myPrio = iter->second; + reclist_t r; + + // RRs with weight zero go to the front of the intermediate + // list, so they'll have little chance of being chosen. + // Larger weights have a higher likelihood of selection. + + for (reclist_t::iterator i = myPrio.begin(); i != myPrio.end(); ) + { + if ((*i)->weight() == 0) + { + r.push_back(*i); + i = myPrio.erase(i); + } else { + ++i; + } + } + + r.insert(r.end(), myPrio.begin(), myPrio.end()); + + while (!r.empty()) + { + U32 total = 0; + + for (reclist_t::const_iterator i = r.begin(); i != r.end(); ++i) + { + total += (*i)->weight(); + } + + U32 target = total > 1 ? (rand() % total) : 0; + U32 partial = 0; + + for (reclist_t::iterator i = r.begin(); i != r.end(); ) + { + partial += (*i)->weight(); + if (partial >= target) + { + newRecs.push_back(**i); + i = r.erase(i); + } else { + ++i; + } + } + } + } + + // Order RRs by lowest numeric priority. The stable sort + // preserves the weight choices we made above. + + stable_sort(newRecs.begin(), newRecs.end(), + LLSRVRecord::ComparePriorityLowest()); + + return newRecs; +} + vector<string> LLSRV::rewriteURI(const string& uriStr) { LLURI uri(uriStr); @@ -166,6 +320,14 @@ vector<string> LLSRV::rewriteURI(const string& uriStr) size_t i; llinfos << "Got " << srvs.size() << " results" << llendl; + for (iter = srvs.begin(); iter != srvs.end(); ++iter) + { + lldebugs << "host " << iter->target() << ':' << iter->port() + << " prio " << iter->priority() + << " weight " << iter->weight() + << llendl; + } + if (srvs.size() > maxSrvs) { llinfos << "Clamping to " << maxSrvs << llendl; |