diff options
Diffstat (limited to '')
-rw-r--r-- | linden/indra/newview/llsrv.cpp | 353 |
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 | ||
33 | using namespace std; | 37 | struct Responder : public LLAres::UriRewriteResponder |
34 | |||
35 | #if LL_WINDOWS | ||
36 | |||
37 | #undef UNICODE | ||
38 | #include <winsock2.h> | ||
39 | #include <windns.h> | ||
40 | |||
41 | vector<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 | |||
86 | vector<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. | ||
150 | bail: | ||
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 | |||
159 | vector<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 | |||
207 | bail: | ||
208 | return reorder(recs); | ||
209 | } | ||
210 | |||
211 | #endif // HOMEGROWN_RESPONSE_PARSER | ||
212 | |||
213 | vector<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); | ||
236 | bail: | ||
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. | ||
244 | vector<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(), | 49 | std::vector<std::string> LLSRV::rewriteURI(const std::string& uri) |
316 | LLSRVRecord::ComparePriorityLowest()); | ||
317 | |||
318 | return newRecs; | ||
319 | } | ||
320 | |||
321 | vector<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 | } |