llsrv.cpp

Go to the documentation of this file.
00001 
00029 #include "llviewerprecompiledheaders.h"
00030 
00031 #include "llsrv.h"
00032 
00033 using namespace std;
00034 
00035 #if LL_WINDOWS
00036 
00037 #undef UNICODE
00038 #include <winsock2.h>
00039 #include <windns.h>
00040 
00041 vector<LLSRVRecord> LLSRV::query(const string& name)
00042 {
00043         vector<LLSRVRecord> recs;
00044         DNS_RECORD *rec;
00045         DNS_STATUS status;
00046 
00047         status = DnsQuery(name.c_str(), DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &rec, NULL);
00048         if (!status)
00049         {
00050                 for (DNS_RECORD *cur = rec; cur != NULL; cur = cur->pNext)
00051                 {
00052                         if (cur->wType != DNS_TYPE_SRV)
00053                         {
00054                                 continue;
00055                         }
00056                         recs.push_back(LLSRVRecord(cur->Data.Srv.wPriority,
00057                                                                            cur->Data.Srv.wWeight,
00058                                                                            cur->Data.Srv.pNameTarget,
00059                                                                            cur->Data.Srv.wPort));
00060                 }
00061                 DnsRecordListFree(rec, DnsFreeRecordListDeep);
00062         }
00063 
00064         return recs;
00065 }
00066 
00067 #else // !LL_WINDOWS
00068 
00069 #include <netinet/in.h>
00070 #include <arpa/nameser.h>
00071 #include <arpa/nameser_compat.h>
00072 #include <resolv.h>
00073 
00074 #include <netdb.h>
00075 
00076 #ifdef HOMEGROWN_RESPONSE_PARSER
00077 
00078 // We ought to be using libresolv's ns_initparse and ns_parserr to
00079 // parse the result of our query.  However, libresolv isn't packaged
00080 // correctly on Linux (as of BIND 9), so neither of these functions is
00081 // available without statically linking against libresolv.  Ugh!  This
00082 // fallback function is available if we need to parse the response
00083 // ourselves without relying too much on libresolv.  It is NOT THE
00084 // DEFAULT.
00085 
00086 vector<LLSRVRecord> LLSRV::parseResponse(const unsigned char *response,
00087                                                                                  int resp_len)
00088 {
00089         vector<LLSRVRecord> recs;
00090 
00091         const unsigned char *pos = response + sizeof(HEADER);
00092         const unsigned char *end = response + resp_len;
00093         const HEADER *hdr = (const HEADER *) response;
00094         char name[1024];
00095 
00096         // Skip over the query embedded in the response.
00097 
00098         for (int q = ntohs(hdr->qdcount); q > 0; --q)
00099         {
00100                 int len = dn_expand(response, end, pos, name, sizeof(name));
00101 
00102                 if (len == -1)
00103                 {
00104                         llinfos << "Could not expand queried name in RR response"
00105                                         << llendl;
00106                         goto bail;
00107                 }
00108                 
00109                 pos += len + NS_QFIXEDSZ;
00110         }
00111         
00112         for (int a = ntohs(hdr->ancount); a > 0; --a)
00113         {
00114                 static const ns_rr *rr;
00115 
00116                 int len = dn_expand(response, end, pos, name, sizeof(name) - 1);
00117                 if (len == -1)
00118                 {
00119                         llinfos << "Could not expand response name in RR response"
00120                                         << llendl;
00121                         goto bail;
00122                 }
00123 
00124                 // Skip over the resource name and headers we don't care about.
00125 
00126                 pos += len + sizeof(rr->type) + sizeof(rr->rr_class) +
00127                         sizeof(rr->ttl) + sizeof(rr->rdlength);
00128                 
00129                 U16 prio;
00130                 U16 weight;
00131                 U16 port;
00132 
00133                 NS_GET16(prio, pos);
00134                 NS_GET16(weight, pos);
00135                 NS_GET16(port, pos);
00136                 
00137                 len = dn_expand(response, end, pos, name, sizeof(name) - 1);
00138 
00139                 if (len == -1)
00140                 {
00141                         llinfos << "Could not expand name in RR response" << llendl;
00142                         goto bail;
00143                 }
00144 
00145                 recs.push_back(LLSRVRecord(prio, weight, name, port));
00146         }
00147 
00148         // There are likely to be more records in the response, but we
00149         // don't care about those, at least for now.
00150 bail:
00151         return reorder(recs);
00152 }
00153 
00154 #else // HOMEGROWN_RESPONSE_PARSER
00155 
00156 // This version of the response parser is the one to use if libresolv
00157 // is available and behaving itself.
00158 
00159 vector<LLSRVRecord> LLSRV::parseResponse(const unsigned char *response,
00160                                                                                  int resp_len)
00161 {
00162         vector<LLSRVRecord> recs;
00163         ns_msg hdr;
00164 
00165         if (ns_initparse(response, resp_len, &hdr))
00166         {
00167                 llinfos << "Could not parse response" << llendl;
00168                 goto bail;
00169         }
00170         
00171         for (int i = 0; i < ns_msg_count(hdr, ns_s_an); i++)
00172         {
00173                 ns_rr rr;
00174                 
00175                 if (ns_parserr(&hdr, ns_s_an, i, &rr))
00176                 {
00177                         llinfos << "Could not parse RR" << llendl;
00178                         goto bail;
00179                 }
00180 
00181                 if (ns_rr_type(rr) != ns_t_srv)
00182                 {
00183                         continue;
00184                 }
00185 
00186                 const unsigned char *pos = ns_rr_rdata(rr);
00187                 U16 prio, weight, port;
00188                 char name[1024];
00189                 int ret;
00190                 
00191                 NS_GET16(prio, pos);
00192                 NS_GET16(weight, pos);
00193                 NS_GET16(port, pos);
00194                 
00195                 ret = dn_expand(ns_msg_base(hdr), ns_msg_end(hdr), pos,
00196                                                 name, sizeof(name));
00197 
00198                 if (ret == -1)
00199                 {
00200                         llinfos << "Could not decompress name" << llendl;
00201                         goto bail;
00202                 }
00203 
00204                 recs.push_back(LLSRVRecord(prio, weight, name, port));
00205         }
00206         
00207 bail:
00208         return reorder(recs);
00209 }
00210 
00211 #endif // HOMEGROWN_RESPONSE_PARSER
00212 
00213 vector<LLSRVRecord> LLSRV::query(const string& queryName)
00214 {
00215         unsigned char response[16384];
00216         vector<LLSRVRecord> recs;
00217         int len;
00218         
00219         len = res_query(queryName.c_str(), ns_c_in, ns_t_srv, response,
00220                                         sizeof(response));
00221 
00222         if (len == -1)
00223         {
00224                 llinfos << "Query failed for " << queryName << llendl;
00225                 goto bail;
00226         }
00227         else if (len > (int) sizeof(response))
00228         {
00229                 llinfos << "Response too big for " << queryName
00230                                 << " (capacity " << sizeof(response)
00231                                 << ", response " << len << ")" << llendl;
00232                 goto bail;
00233         }
00234 
00235         recs = parseResponse(response, len);
00236 bail:
00237         return reorder(recs);
00238 }
00239 
00240 #endif // LL_WINDOWS
00241 
00242 // Implement the algorithm specified in RFC 2782 for dealing with RRs
00243 // of differing priorities and weights.
00244 vector<LLSRVRecord> LLSRV::reorder(vector<LLSRVRecord>& recs)
00245 {
00246         typedef list<const LLSRVRecord *> reclist_t;
00247         typedef map<U16, reclist_t> bucket_t;
00248         vector<LLSRVRecord> newRecs;
00249         bucket_t buckets;
00250 
00251         // Don't rely on the DNS server to shuffle responses.
00252         
00253         random_shuffle(recs.begin(), recs.end());
00254 
00255         for (vector<LLSRVRecord>::const_iterator iter = recs.begin();
00256                  iter != recs.end(); ++iter)
00257         {
00258                 buckets[iter->priority()].push_back(&*iter);
00259         }
00260         
00261         // Priorities take precedence over weights.
00262 
00263         for (bucket_t::iterator iter = buckets.begin();
00264                  iter != buckets.end(); ++iter)
00265         {
00266                 reclist_t& myPrio = iter->second;
00267                 reclist_t r;
00268 
00269                 // RRs with weight zero go to the front of the intermediate
00270                 // list, so they'll have little chance of being chosen.
00271                 // Larger weights have a higher likelihood of selection.
00272 
00273                 for (reclist_t::iterator i = myPrio.begin(); i != myPrio.end(); )
00274                 {
00275                         if ((*i)->weight() == 0)
00276                         {
00277                                 r.push_back(*i);
00278                                 i = myPrio.erase(i);
00279                         } else {
00280                                 ++i;
00281                         }
00282                 }
00283 
00284                 r.insert(r.end(), myPrio.begin(), myPrio.end());
00285                 
00286                 while (!r.empty())
00287                 {
00288                         U32 total = 0;
00289 
00290                         for (reclist_t::const_iterator i = r.begin(); i != r.end(); ++i)
00291                         {
00292                                 total += (*i)->weight();
00293                         }
00294 
00295                         U32 target = total > 1 ? (rand() % total) : 0;
00296                         U32 partial = 0;
00297                         
00298                         for (reclist_t::iterator i = r.begin(); i != r.end(); )
00299                         {
00300                                 partial += (*i)->weight();
00301                                 if (partial >= target)
00302                                 {
00303                                         newRecs.push_back(**i);
00304                                         i = r.erase(i);
00305                                 } else {
00306                                         ++i;
00307                                 }
00308                         }
00309                 }
00310         }
00311         
00312         // Order RRs by lowest numeric priority.  The stable sort
00313         // preserves the weight choices we made above.
00314 
00315         stable_sort(newRecs.begin(), newRecs.end(),
00316                                 LLSRVRecord::ComparePriorityLowest());
00317 
00318         return newRecs;
00319 }
00320 
00321 vector<string> LLSRV::rewriteURI(const string& uriStr)
00322 {
00323         LLURI uri(uriStr);
00324         const string& scheme = uri.scheme();
00325         llinfos << "Rewriting " << uriStr << llendl;
00326         string serviceName("_" + scheme + "._tcp." + uri.hostName());
00327         llinfos << "Querying for " << serviceName << llendl;
00328         vector<LLSRVRecord> srvs(LLSRV::query(serviceName));
00329         vector<string> rewritten;
00330 
00331         if (srvs.empty())
00332         {
00333                 llinfos << "No query results; using " << uriStr << llendl;
00334                 rewritten.push_back(uriStr);
00335         }
00336         else
00337         {
00338                 vector<LLSRVRecord>::const_iterator iter;
00339                 size_t maxSrvs = 3;
00340                 size_t i;
00341 
00342                 llinfos << "Got " << srvs.size() << " results" << llendl;
00343                 for (iter = srvs.begin(); iter != srvs.end(); ++iter)
00344                 {
00345                         lldebugs << "host " << iter->target() << ':' << iter->port()
00346                                          << " prio " << iter->priority()
00347                                          << " weight " << iter->weight()
00348                                          << llendl;
00349                 }
00350 
00351                 if (srvs.size() > maxSrvs)
00352                 {
00353                         llinfos << "Clamping to " << maxSrvs << llendl;
00354                 }
00355 
00356                 for (iter = srvs.begin(), i = 0;
00357                          iter != srvs.end() && i < maxSrvs; ++iter, ++i)
00358                 {
00359                         LLURI newUri(scheme,
00360                                                  uri.userName(),
00361                                                  uri.password(),
00362                                                  iter->target(),
00363                                                  uri.defaultPort() ? iter->port() : uri.hostPort(),
00364                                                  uri.escapedPath(),
00365                                                  uri.escapedQuery());
00366                         string newUriStr(newUri.asString());
00367 
00368                         llinfos << "Rewrite[" << i << "] " << newUriStr << llendl;
00369 
00370                         rewritten.push_back(newUriStr);
00371                 }
00372         }
00373 
00374         return rewritten;
00375 }

Generated on Thu Jul 1 06:09:12 2010 for Second Life Viewer by  doxygen 1.4.7