aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llsrv.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--linden/indra/newview/llsrv.cpp353
1 files changed, 17 insertions, 336 deletions
diff --git a/linden/indra/newview/llsrv.cpp b/linden/indra/newview/llsrv.cpp
index 5a623bc..150fcb1 100644
--- a/linden/indra/newview/llsrv.cpp
+++ b/linden/indra/newview/llsrv.cpp
@@ -2,7 +2,9 @@
2 * @file llsrv.cpp 2 * @file llsrv.cpp
3 * @brief Wrapper for DNS SRV record lookups 3 * @brief Wrapper for DNS SRV record lookups
4 * 4 *
5 * Copyright (c) 2007-2007, Linden Research, Inc. 5 * $LicenseInfo:firstyear=2007&license=viewergpl$
6 *
7 * Copyright (c) 2007, Linden Research, Inc.
6 * 8 *
7 * Second Life Viewer Source Code 9 * Second Life Viewer Source Code
8 * The source code in this file ("Source Code") is provided by Linden Lab 10 * The source code in this file ("Source Code") is provided by Linden Lab
@@ -24,352 +26,31 @@
24 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO 26 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
25 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, 27 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
26 * COMPLETENESS OR PERFORMANCE. 28 * COMPLETENESS OR PERFORMANCE.
29 * $/LicenseInfo$
27 */ 30 */
28 31
29#include "llviewerprecompiledheaders.h" 32#include "llviewerprecompiledheaders.h"
30 33
31#include "llsrv.h" 34#include "llsrv.h"
35#include "llares.h"
32 36
33using namespace std; 37struct Responder : public LLAres::UriRewriteResponder
34
35#if LL_WINDOWS
36
37#undef UNICODE
38#include <winsock2.h>
39#include <windns.h>
40
41vector<LLSRVRecord> LLSRV::query(const string& name)
42{
43 vector<LLSRVRecord> recs;
44 DNS_RECORD *rec;
45 DNS_STATUS status;
46
47 status = DnsQuery(name.c_str(), DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &rec, NULL);
48 if (!status)
49 {
50 for (DNS_RECORD *cur = rec; cur != NULL; cur = cur->pNext)
51 {
52 if (cur->wType != DNS_TYPE_SRV)
53 {
54 continue;
55 }
56 recs.push_back(LLSRVRecord(cur->Data.Srv.wPriority,
57 cur->Data.Srv.wWeight,
58 cur->Data.Srv.pNameTarget,
59 cur->Data.Srv.wPort));
60 }
61 DnsRecordListFree(rec, DnsFreeRecordListDeep);
62 }
63
64 return recs;
65}
66
67#else // !LL_WINDOWS
68
69#include <netinet/in.h>
70#include <arpa/nameser.h>
71#include <arpa/nameser_compat.h>
72#include <resolv.h>
73
74#include <netdb.h>
75
76#ifdef HOMEGROWN_RESPONSE_PARSER
77
78// We ought to be using libresolv's ns_initparse and ns_parserr to
79// parse the result of our query. However, libresolv isn't packaged
80// correctly on Linux (as of BIND 9), so neither of these functions is
81// available without statically linking against libresolv. Ugh! This
82// fallback function is available if we need to parse the response
83// ourselves without relying too much on libresolv. It is NOT THE
84// DEFAULT.
85
86vector<LLSRVRecord> LLSRV::parseResponse(const unsigned char *response,
87 int resp_len)
88{
89 vector<LLSRVRecord> recs;
90
91 const unsigned char *pos = response + sizeof(HEADER);
92 const unsigned char *end = response + resp_len;
93 const HEADER *hdr = (const HEADER *) response;
94 char name[1024];
95
96 // Skip over the query embedded in the response.
97
98 for (int q = ntohs(hdr->qdcount); q > 0; --q)
99 {
100 int len = dn_expand(response, end, pos, name, sizeof(name));
101
102 if (len == -1)
103 {
104 llinfos << "Could not expand queried name in RR response"
105 << llendl;
106 goto bail;
107 }
108
109 pos += len + NS_QFIXEDSZ;
110 }
111
112 for (int a = ntohs(hdr->ancount); a > 0; --a)
113 {
114 static const ns_rr *rr;
115
116 int len = dn_expand(response, end, pos, name, sizeof(name) - 1);
117 if (len == -1)
118 {
119 llinfos << "Could not expand response name in RR response"
120 << llendl;
121 goto bail;
122 }
123
124 // Skip over the resource name and headers we don't care about.
125
126 pos += len + sizeof(rr->type) + sizeof(rr->rr_class) +
127 sizeof(rr->ttl) + sizeof(rr->rdlength);
128
129 U16 prio;
130 U16 weight;
131 U16 port;
132
133 NS_GET16(prio, pos);
134 NS_GET16(weight, pos);
135 NS_GET16(port, pos);
136
137 len = dn_expand(response, end, pos, name, sizeof(name) - 1);
138
139 if (len == -1)
140 {
141 llinfos << "Could not expand name in RR response" << llendl;
142 goto bail;
143 }
144
145 recs.push_back(LLSRVRecord(prio, weight, name, port));
146 }
147
148 // There are likely to be more records in the response, but we
149 // don't care about those, at least for now.
150bail:
151 return reorder(recs);
152}
153
154#else // HOMEGROWN_RESPONSE_PARSER
155
156// This version of the response parser is the one to use if libresolv
157// is available and behaving itself.
158
159vector<LLSRVRecord> LLSRV::parseResponse(const unsigned char *response,
160 int resp_len)
161{
162 vector<LLSRVRecord> recs;
163 ns_msg hdr;
164
165 if (ns_initparse(response, resp_len, &hdr))
166 {
167 llinfos << "Could not parse response" << llendl;
168 goto bail;
169 }
170
171 for (int i = 0; i < ns_msg_count(hdr, ns_s_an); i++)
172 {
173 ns_rr rr;
174
175 if (ns_parserr(&hdr, ns_s_an, i, &rr))
176 {
177 llinfos << "Could not parse RR" << llendl;
178 goto bail;
179 }
180
181 if (ns_rr_type(rr) != ns_t_srv)
182 {
183 continue;
184 }
185
186 const unsigned char *pos = ns_rr_rdata(rr);
187 U16 prio, weight, port;
188 char name[1024];
189 int ret;
190
191 NS_GET16(prio, pos);
192 NS_GET16(weight, pos);
193 NS_GET16(port, pos);
194
195 ret = dn_expand(ns_msg_base(hdr), ns_msg_end(hdr), pos,
196 name, sizeof(name));
197
198 if (ret == -1)
199 {
200 llinfos << "Could not decompress name" << llendl;
201 goto bail;
202 }
203
204 recs.push_back(LLSRVRecord(prio, weight, name, port));
205 }
206
207bail:
208 return reorder(recs);
209}
210
211#endif // HOMEGROWN_RESPONSE_PARSER
212
213vector<LLSRVRecord> LLSRV::query(const string& queryName)
214{
215 unsigned char response[16384];
216 vector<LLSRVRecord> recs;
217 int len;
218
219 len = res_query(queryName.c_str(), ns_c_in, ns_t_srv, response,
220 sizeof(response));
221
222 if (len == -1)
223 {
224 llinfos << "Query failed for " << queryName << llendl;
225 goto bail;
226 }
227 else if (len > (int) sizeof(response))
228 {
229 llinfos << "Response too big for " << queryName
230 << " (capacity " << sizeof(response)
231 << ", response " << len << ")" << llendl;
232 goto bail;
233 }
234
235 recs = parseResponse(response, len);
236bail:
237 return reorder(recs);
238}
239
240#endif // LL_WINDOWS
241
242// Implement the algorithm specified in RFC 2782 for dealing with RRs
243// of differing priorities and weights.
244vector<LLSRVRecord> LLSRV::reorder(vector<LLSRVRecord>& recs)
245{ 38{
246 typedef list<const LLSRVRecord *> reclist_t; 39 std::vector<std::string> mUris;
247 typedef map<U16, reclist_t> bucket_t; 40 void rewriteResult(const std::vector<std::string> &uris) {
248 vector<LLSRVRecord> newRecs; 41 for (size_t i = 0; i < uris.size(); i++)
249 bucket_t buckets;
250
251 // Don't rely on the DNS server to shuffle responses.
252
253 random_shuffle(recs.begin(), recs.end());
254
255 for (vector<LLSRVRecord>::const_iterator iter = recs.begin();
256 iter != recs.end(); ++iter)
257 {
258 buckets[iter->priority()].push_back(&*iter);
259 }
260
261 // Priorities take precedence over weights.
262
263 for (bucket_t::iterator iter = buckets.begin();
264 iter != buckets.end(); ++iter)
265 {
266 reclist_t& myPrio = iter->second;
267 reclist_t r;
268
269 // RRs with weight zero go to the front of the intermediate
270 // list, so they'll have little chance of being chosen.
271 // Larger weights have a higher likelihood of selection.
272
273 for (reclist_t::iterator i = myPrio.begin(); i != myPrio.end(); )
274 {
275 if ((*i)->weight() == 0)
276 {
277 r.push_back(*i);
278 i = myPrio.erase(i);
279 } else {
280 ++i;
281 }
282 }
283
284 r.insert(r.end(), myPrio.begin(), myPrio.end());
285
286 while (!r.empty())
287 { 42 {
288 U32 total = 0; 43 llinfos << "[" << i << "] " << uris[i] << llendl;
289
290 for (reclist_t::const_iterator i = r.begin(); i != r.end(); ++i)
291 {
292 total += (*i)->weight();
293 }
294
295 U32 target = total > 1 ? (rand() % total) : 0;
296 U32 partial = 0;
297
298 for (reclist_t::iterator i = r.begin(); i != r.end(); )
299 {
300 partial += (*i)->weight();
301 if (partial >= target)
302 {
303 newRecs.push_back(**i);
304 i = r.erase(i);
305 } else {
306 ++i;
307 }
308 }
309 } 44 }
45 mUris = uris;
310 } 46 }
311 47};
312 // Order RRs by lowest numeric priority. The stable sort
313 // preserves the weight choices we made above.
314 48
315 stable_sort(newRecs.begin(), newRecs.end(), 49std::vector<std::string> LLSRV::rewriteURI(const std::string& uri)
316 LLSRVRecord::ComparePriorityLowest());
317
318 return newRecs;
319}
320
321vector<string> LLSRV::rewriteURI(const string& uriStr)
322{ 50{
323 LLURI uri(uriStr); 51 LLPointer<Responder> resp = new Responder;
324 const string& scheme = uri.scheme();
325 llinfos << "Rewriting " << uriStr << llendl;
326 string serviceName("_" + scheme + "._tcp." + uri.hostName());
327 llinfos << "Querying for " << serviceName << llendl;
328 vector<LLSRVRecord> srvs(LLSRV::query(serviceName));
329 vector<string> rewritten;
330
331 if (srvs.empty())
332 {
333 llinfos << "No query results; using " << uriStr << llendl;
334 rewritten.push_back(uriStr);
335 }
336 else
337 {
338 vector<LLSRVRecord>::const_iterator iter;
339 size_t maxSrvs = 3;
340 size_t i;
341
342 llinfos << "Got " << srvs.size() << " results" << llendl;
343 for (iter = srvs.begin(); iter != srvs.end(); ++iter)
344 {
345 lldebugs << "host " << iter->target() << ':' << iter->port()
346 << " prio " << iter->priority()
347 << " weight " << iter->weight()
348 << llendl;
349 }
350
351 if (srvs.size() > maxSrvs)
352 {
353 llinfos << "Clamping to " << maxSrvs << llendl;
354 }
355
356 for (iter = srvs.begin(), i = 0;
357 iter != srvs.end() && i < maxSrvs; ++iter, ++i)
358 {
359 LLURI newUri(scheme,
360 uri.userName(),
361 uri.password(),
362 iter->target(),
363 uri.defaultPort() ? iter->port() : uri.hostPort(),
364 uri.escapedPath(),
365 uri.escapedQuery());
366 string newUriStr(newUri.asString());
367
368 llinfos << "Rewrite[" << i << "] " << newUriStr << llendl;
369
370 rewritten.push_back(newUriStr);
371 }
372 }
373 52
374 return rewritten; 53 gAres->rewriteURI(uri, resp);
54 gAres->processAll();
55 return resp->mUris;
375} 56}