/src/mozilla-central/netwerk/dns/TRR.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim:set ts=4 sw=4 sts=4 et cin: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "DNS.h" |
8 | | #include "nsCharSeparatedTokenizer.h" |
9 | | #include "nsContentUtils.h" |
10 | | #include "nsHostResolver.h" |
11 | | #include "nsIHttpChannel.h" |
12 | | #include "nsIHttpChannelInternal.h" |
13 | | #include "nsIIOService.h" |
14 | | #include "nsIInputStream.h" |
15 | | #include "nsISupportsBase.h" |
16 | | #include "nsISupportsUtils.h" |
17 | | #include "nsIUploadChannel2.h" |
18 | | #include "nsNetUtil.h" |
19 | | #include "nsStringStream.h" |
20 | | #include "nsThreadUtils.h" |
21 | | #include "nsURLHelper.h" |
22 | | #include "TRR.h" |
23 | | #include "TRRService.h" |
24 | | |
25 | | #include "mozilla/Base64.h" |
26 | | #include "mozilla/DebugOnly.h" |
27 | | #include "mozilla/Logging.h" |
28 | | #include "mozilla/Preferences.h" |
29 | | #include "mozilla/Telemetry.h" |
30 | | #include "mozilla/TimeStamp.h" |
31 | | #include "mozilla/Tokenizer.h" |
32 | | |
33 | | namespace mozilla { |
34 | | namespace net { |
35 | | |
36 | | #undef LOG |
37 | | extern mozilla::LazyLogModule gHostResolverLog; |
38 | 0 | #define LOG(args) MOZ_LOG(gHostResolverLog, mozilla::LogLevel::Debug, args) |
39 | 0 | #define LOG_ENABLED() MOZ_LOG_TEST(mozilla::net::gHostResolverLog, mozilla::LogLevel::Debug) |
40 | | |
41 | | NS_IMPL_ISUPPORTS(TRR, nsIHttpPushListener, nsIInterfaceRequestor, nsIStreamListener, nsIRunnable) |
42 | | |
43 | | const uint8_t kDNS_CLASS_IN = 1; |
44 | | |
45 | | NS_IMETHODIMP |
46 | | TRR::Notify(nsITimer *aTimer) |
47 | 0 | { |
48 | 0 | if (aTimer == mTimeout) { |
49 | 0 | mTimeout = nullptr; |
50 | 0 | Cancel(); |
51 | 0 | } else { |
52 | 0 | MOZ_CRASH("Unknown timer"); |
53 | 0 | } |
54 | 0 |
|
55 | 0 | return NS_OK; |
56 | 0 | } |
57 | | |
58 | | // convert a given host request to a DOH 'body' |
59 | | // |
60 | | nsresult |
61 | | TRR::DohEncode(nsCString &aBody, bool aDisableECS) |
62 | 0 | { |
63 | 0 | aBody.Truncate(); |
64 | 0 | // Header |
65 | 0 | aBody += '\0'; |
66 | 0 | aBody += '\0'; // 16 bit id |
67 | 0 | aBody += 0x01; // |QR| Opcode |AA|TC|RD| Set the RD bit |
68 | 0 | aBody += '\0'; // |RA| Z | RCODE | |
69 | 0 | aBody += '\0'; |
70 | 0 | aBody += 1; // QDCOUNT (number of entries in the question section) |
71 | 0 | aBody += '\0'; |
72 | 0 | aBody += '\0'; // ANCOUNT |
73 | 0 | aBody += '\0'; |
74 | 0 | aBody += '\0'; // NSCOUNT |
75 | 0 |
|
76 | 0 | aBody += '\0'; // ARCOUNT |
77 | 0 | aBody += aDisableECS ? 1 : '\0'; // ARCOUNT low byte for EDNS(0) |
78 | 0 |
|
79 | 0 | // Question |
80 | 0 |
|
81 | 0 | // The input host name should be converted to a sequence of labels, where |
82 | 0 | // each label consists of a length octet followed by that number of |
83 | 0 | // octets. The domain name terminates with the zero length octet for the |
84 | 0 | // null label of the root. |
85 | 0 | // Followed by 16 bit QTYPE and 16 bit QCLASS |
86 | 0 |
|
87 | 0 | int32_t index = 0; |
88 | 0 | int32_t offset = 0; |
89 | 0 | do { |
90 | 0 | bool dotFound = false; |
91 | 0 | int32_t labelLength; |
92 | 0 | index = mHost.FindChar('.', offset); |
93 | 0 | if (kNotFound != index) { |
94 | 0 | dotFound = true; |
95 | 0 | labelLength = index - offset; |
96 | 0 | } else { |
97 | 0 | labelLength = mHost.Length() - offset; |
98 | 0 | } |
99 | 0 | if (labelLength > 63) { |
100 | 0 | // too long label! |
101 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
102 | 0 | } |
103 | 0 | aBody += static_cast<unsigned char>(labelLength); |
104 | 0 | nsDependentCSubstring label = Substring(mHost, offset, labelLength); |
105 | 0 | aBody.Append(label); |
106 | 0 | if(!dotFound) { |
107 | 0 | aBody += '\0'; // terminate with a final zero |
108 | 0 | break; |
109 | 0 | } |
110 | 0 | offset += labelLength + 1; // move over label and dot |
111 | 0 | } while(true); |
112 | 0 |
|
113 | 0 | aBody += '\0'; // upper 8 bit TYPE |
114 | 0 | aBody += static_cast<uint8_t>(mType); |
115 | 0 | aBody += '\0'; // upper 8 bit CLASS |
116 | 0 | aBody += kDNS_CLASS_IN; // IN - "the Internet" |
117 | 0 |
|
118 | 0 | if (aDisableECS) { |
119 | 0 | // EDNS(0) is RFC 6891, ECS is RFC 7871 |
120 | 0 | aBody += '\0'; // NAME | domain name | MUST be 0 (root domain) | |
121 | 0 | aBody += '\0'; |
122 | 0 | aBody += 41; // TYPE | u_int16_t | OPT (41) | |
123 | 0 | aBody += 16; // CLASS | u_int16_t | requestor's UDP payload size | |
124 | 0 | aBody += '\0'; // advertise 4K (high-byte: 16 | low-byte: 0), ignored by DoH |
125 | 0 | aBody += '\0'; // TTL | u_int32_t | extended RCODE and flags | |
126 | 0 | aBody += '\0'; |
127 | 0 | aBody += '\0'; |
128 | 0 | aBody += '\0'; |
129 | 0 |
|
130 | 0 | aBody += '\0'; // upper 8 bit RDLEN |
131 | 0 | aBody += 8; // RDLEN | u_int16_t | length of all RDATA | |
132 | 0 |
|
133 | 0 | // RDATA | octet stream | {attribute,value} pairs | |
134 | 0 | // The RDATA is just the ECS option setting zero subnet prefix |
135 | 0 |
|
136 | 0 | aBody += '\0'; // upper 8 bit OPTION-CODE ECS |
137 | 0 | aBody += 8; // OPTION-CODE, 2 octets, for ECS is 8 |
138 | 0 |
|
139 | 0 | aBody += '\0'; // upper 8 bit OPTION-LENGTH |
140 | 0 | aBody += 4; // OPTION-LENGTH, 2 octets, contains the length of the payload |
141 | 0 | // after OPTION-LENGTH |
142 | 0 | aBody += '\0'; // upper 8 bit FAMILY. IANA Address Family Numbers registry, not the |
143 | 0 | // AF_* constants! |
144 | 0 | aBody += 1; // FAMILY (Ipv4), 2 octets |
145 | 0 |
|
146 | 0 | aBody += '\0'; // SOURCE PREFIX-LENGTH | SCOPE PREFIX-LENGTH | |
147 | 0 | aBody += '\0'; |
148 | 0 |
|
149 | 0 | // ADDRESS, minimum number of octets == nothing because zero bits |
150 | 0 | } |
151 | 0 | return NS_OK; |
152 | 0 | } |
153 | | |
154 | | NS_IMETHODIMP |
155 | | TRR::Run() |
156 | 0 | { |
157 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
158 | 0 | if ((gTRRService == nullptr) || NS_FAILED(SendHTTPRequest())) { |
159 | 0 | FailData(NS_ERROR_FAILURE); |
160 | 0 | // The dtor will now be run |
161 | 0 | } |
162 | 0 | return NS_OK; |
163 | 0 | } |
164 | | |
165 | | nsresult |
166 | | TRR::SendHTTPRequest() |
167 | 0 | { |
168 | 0 | // This is essentially the "run" method - created from nsHostResolver |
169 | 0 | MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); |
170 | 0 |
|
171 | 0 | if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) && (mType != TRRTYPE_NS) && |
172 | 0 | (mType != TRRTYPE_TXT)) { |
173 | 0 | // limit the calling interface because nsHostResolver has explicit slots for |
174 | 0 | // these types |
175 | 0 | return NS_ERROR_FAILURE; |
176 | 0 | } |
177 | 0 | |
178 | 0 | if ((mType == TRRTYPE_A) || (mType == TRRTYPE_AAAA)) { |
179 | 0 | // let NS resolves skip the blacklist check |
180 | 0 | if (gTRRService->IsTRRBlacklisted(mHost, mPB, true)) { |
181 | 0 | if (mType == TRRTYPE_A) { |
182 | 0 | // count only blacklist for A records to avoid double counts |
183 | 0 | Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED, true); |
184 | 0 | } |
185 | 0 | // not really an error but no TRR is issued |
186 | 0 | return NS_ERROR_UNKNOWN_HOST; |
187 | 0 | } else { |
188 | 0 | if (mType == TRRTYPE_A) { |
189 | 0 | Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED, false); |
190 | 0 | } |
191 | 0 | } |
192 | 0 | } |
193 | 0 |
|
194 | 0 | nsresult rv; |
195 | 0 | nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv)); |
196 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
197 | 0 |
|
198 | 0 | bool useGet = gTRRService->UseGET(); |
199 | 0 | nsAutoCString body; |
200 | 0 | nsCOMPtr<nsIURI> dnsURI; |
201 | 0 | bool disableECS = gTRRService->DisableECS(); |
202 | 0 |
|
203 | 0 | LOG(("TRR::SendHTTPRequest resolve %s type %u\n", mHost.get(), mType)); |
204 | 0 |
|
205 | 0 | if (useGet) { |
206 | 0 | nsAutoCString tmp; |
207 | 0 | rv = DohEncode(tmp, disableECS); |
208 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
209 | 0 |
|
210 | 0 | /* For GET requests, the outgoing packet needs to be Base64url-encoded and |
211 | 0 | then appended to the end of the URI. */ |
212 | 0 | rv = Base64URLEncode(tmp.Length(), reinterpret_cast<const unsigned char *>(tmp.get()), |
213 | 0 | Base64URLEncodePaddingPolicy::Omit, body); |
214 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
215 | 0 |
|
216 | 0 | nsAutoCString uri; |
217 | 0 | gTRRService->GetURI(uri); |
218 | 0 | uri.Append(NS_LITERAL_CSTRING("?dns=")); |
219 | 0 | uri.Append(body); |
220 | 0 | LOG(("TRR::SendHTTPRequest GET dns=%s\n", body.get())); |
221 | 0 | rv = NS_NewURI(getter_AddRefs(dnsURI), uri); |
222 | 0 | } else { |
223 | 0 | rv = DohEncode(body, disableECS); |
224 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
225 | 0 |
|
226 | 0 | nsAutoCString uri; |
227 | 0 | gTRRService->GetURI(uri); |
228 | 0 | rv = NS_NewURI(getter_AddRefs(dnsURI), uri); |
229 | 0 | } |
230 | 0 | if (NS_FAILED(rv)) { |
231 | 0 | LOG(("TRR:SendHTTPRequest: NewURI failed!\n")); |
232 | 0 | return rv; |
233 | 0 | } |
234 | 0 |
|
235 | 0 | rv = NS_NewChannel(getter_AddRefs(mChannel), |
236 | 0 | dnsURI, |
237 | 0 | nsContentUtils::GetSystemPrincipal(), |
238 | 0 | nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, |
239 | 0 | nsIContentPolicy::TYPE_OTHER, |
240 | 0 | nullptr, // PerformanceStorage |
241 | 0 | nullptr, // aLoadGroup |
242 | 0 | this, |
243 | 0 | nsIRequest::LOAD_ANONYMOUS | |
244 | 0 | (mPB ? nsIRequest::INHIBIT_CACHING: 0), ios); |
245 | 0 | if (NS_FAILED(rv)) { |
246 | 0 | LOG(("TRR:SendHTTPRequest: NewChannel failed!\n")); |
247 | 0 | return rv; |
248 | 0 | } |
249 | 0 |
|
250 | 0 | nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel); |
251 | 0 | if (!httpChannel) { |
252 | 0 | return NS_ERROR_UNEXPECTED; |
253 | 0 | } |
254 | 0 | |
255 | 0 | rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), |
256 | 0 | NS_LITERAL_CSTRING("application/dns-message"), |
257 | 0 | false); |
258 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
259 | 0 |
|
260 | 0 | nsAutoCString cred; |
261 | 0 | gTRRService->GetCredentials(cred); |
262 | 0 | if (!cred.IsEmpty()){ |
263 | 0 | rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Authorization"), cred, false); |
264 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
265 | 0 | } |
266 | 0 |
|
267 | 0 | nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(mChannel); |
268 | 0 | if (!internalChannel) { |
269 | 0 | return NS_ERROR_UNEXPECTED; |
270 | 0 | } |
271 | 0 | |
272 | 0 | // setting a small stream window means the h2 stack won't pipeline a window update |
273 | 0 | // with each HEADERS or reply to a DATA with a WINDOW UPDATE |
274 | 0 | rv = internalChannel->SetInitialRwin(127 * 1024); |
275 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
276 | 0 | rv = internalChannel->SetTrr(true); |
277 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
278 | 0 |
|
279 | 0 | mAllowRFC1918 = gTRRService->AllowRFC1918(); |
280 | 0 |
|
281 | 0 | if (useGet) { |
282 | 0 | rv = httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("GET")); |
283 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
284 | 0 | } else { |
285 | 0 | rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Cache-Control"), |
286 | 0 | NS_LITERAL_CSTRING("no-store"), false); |
287 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
288 | 0 | nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel); |
289 | 0 | if (!uploadChannel) { |
290 | 0 | return NS_ERROR_UNEXPECTED; |
291 | 0 | } |
292 | 0 | uint32_t streamLength = body.Length(); |
293 | 0 | nsCOMPtr<nsIInputStream> uploadStream; |
294 | 0 | rv = NS_NewCStringInputStream(getter_AddRefs(uploadStream), std::move(body)); |
295 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
296 | 0 |
|
297 | 0 | rv = uploadChannel->ExplicitSetUploadStream(uploadStream, |
298 | 0 | NS_LITERAL_CSTRING("application/dns-message"), |
299 | 0 | streamLength, |
300 | 0 | NS_LITERAL_CSTRING("POST"), false); |
301 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
302 | 0 | } |
303 | 0 |
|
304 | 0 | // set the *default* response content type |
305 | 0 | if (NS_FAILED(httpChannel->SetContentType(NS_LITERAL_CSTRING("application/dns-message")))) { |
306 | 0 | LOG(("TRR::SendHTTPRequest: couldn't set content-type!\n")); |
307 | 0 | } |
308 | 0 | if (NS_SUCCEEDED(httpChannel->AsyncOpen2(this))) { |
309 | 0 | NS_NewTimerWithCallback(getter_AddRefs(mTimeout), |
310 | 0 | this, gTRRService->GetRequestTimeout(), |
311 | 0 | nsITimer::TYPE_ONE_SHOT); |
312 | 0 | return NS_OK; |
313 | 0 | } |
314 | 0 | mChannel = nullptr; |
315 | 0 | return NS_ERROR_UNEXPECTED; |
316 | 0 | } |
317 | | |
318 | | NS_IMETHODIMP |
319 | | TRR::GetInterface(const nsIID &iid, void **result) |
320 | 0 | { |
321 | 0 | if (!iid.Equals(NS_GET_IID(nsIHttpPushListener))) { |
322 | 0 | return NS_ERROR_NO_INTERFACE; |
323 | 0 | } |
324 | 0 | |
325 | 0 | nsCOMPtr<nsIHttpPushListener> copy(this); |
326 | 0 | *result = copy.forget().take(); |
327 | 0 | return NS_OK; |
328 | 0 | } |
329 | | |
330 | | nsresult |
331 | | TRR::DohDecodeQuery(const nsCString &query, nsCString &host, enum TrrType &type) |
332 | 0 | { |
333 | 0 | FallibleTArray<uint8_t> binary; |
334 | 0 | bool found_dns = false; |
335 | 0 | LOG(("TRR::DohDecodeQuery %s!\n", query.get())); |
336 | 0 |
|
337 | 0 | // extract "dns=" from the query string |
338 | 0 | nsCCharSeparatedTokenizer tokenizer(query, '&'); |
339 | 0 | nsAutoCString data; |
340 | 0 | while (tokenizer.hasMoreTokens()) { |
341 | 0 | const nsACString& token = tokenizer.nextToken(); |
342 | 0 | nsDependentCSubstring dns = Substring(token, 0, 4); |
343 | 0 | nsAutoCString check(dns); |
344 | 0 | if (check.Equals("dns=")) { |
345 | 0 | nsDependentCSubstring q = Substring(token, 4, -1); |
346 | 0 | data = q; |
347 | 0 | found_dns = true; |
348 | 0 | break; |
349 | 0 | } |
350 | 0 | } |
351 | 0 | if (!found_dns) { |
352 | 0 | LOG(("TRR::DohDecodeQuery no dns= in pushed URI query string\n")); |
353 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
354 | 0 | } |
355 | 0 |
|
356 | 0 | nsresult rv = Base64URLDecode(data, |
357 | 0 | Base64URLDecodePaddingPolicy::Ignore, binary); |
358 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
359 | 0 | uint32_t avail = binary.Length(); |
360 | 0 | if (avail < 12) { |
361 | 0 | return NS_ERROR_FAILURE; |
362 | 0 | } |
363 | 0 | // check the query bit and the opcode |
364 | 0 | if ((binary[2] & 0xf8) != 0) { |
365 | 0 | return NS_ERROR_FAILURE; |
366 | 0 | } |
367 | 0 | uint32_t qdcount = (binary[4] << 8) + binary[5]; |
368 | 0 | if (!qdcount) { |
369 | 0 | return NS_ERROR_FAILURE; |
370 | 0 | } |
371 | 0 | |
372 | 0 | uint32_t index = 12; |
373 | 0 | uint32_t length = 0; |
374 | 0 | host.Truncate(); |
375 | 0 | do { |
376 | 0 | if (avail < (index + 1)) { |
377 | 0 | return NS_ERROR_UNEXPECTED; |
378 | 0 | } |
379 | 0 | |
380 | 0 | length = binary[index]; |
381 | 0 | if (length) { |
382 | 0 | if (host.Length()) { |
383 | 0 | host.Append("."); |
384 | 0 | } |
385 | 0 | if (avail < (index + 1 + length)) { |
386 | 0 | return NS_ERROR_UNEXPECTED; |
387 | 0 | } |
388 | 0 | host.Append((const char *)(&binary[0]) + index + 1, length); |
389 | 0 | } |
390 | 0 | index += 1 + length; // skip length byte + label |
391 | 0 | } while (length); |
392 | 0 |
|
393 | 0 | LOG(("TRR::DohDecodeQuery host %s\n", host.get())); |
394 | 0 |
|
395 | 0 | if (avail < (index + 2)) { |
396 | 0 | return NS_ERROR_UNEXPECTED; |
397 | 0 | } |
398 | 0 | uint16_t i16 = 0; |
399 | 0 | i16 += binary[index] << 8; |
400 | 0 | i16 += binary[index + 1]; |
401 | 0 | index += 4; // skip question's type, class |
402 | 0 | type = (enum TrrType)i16; |
403 | 0 |
|
404 | 0 | LOG(("TRR::DohDecodeQuery type %d\n", (int)type)); |
405 | 0 |
|
406 | 0 | return NS_OK; |
407 | 0 | } |
408 | | |
409 | | nsresult |
410 | | TRR::ReceivePush(nsIHttpChannel *pushed, nsHostRecord *pushedRec) |
411 | 0 | { |
412 | 0 | if (!mHostResolver) { |
413 | 0 | return NS_ERROR_UNEXPECTED; |
414 | 0 | } |
415 | 0 | |
416 | 0 | LOG(("TRR::ReceivePush: PUSH incoming!\n")); |
417 | 0 |
|
418 | 0 | nsCOMPtr<nsIURI> uri; |
419 | 0 | pushed->GetURI(getter_AddRefs(uri)); |
420 | 0 | nsAutoCString query; |
421 | 0 | if (uri) { |
422 | 0 | uri->GetQuery(query); |
423 | 0 | } |
424 | 0 |
|
425 | 0 | PRNetAddr tempAddr; |
426 | 0 | if (NS_FAILED(DohDecodeQuery(query, mHost, mType)) || |
427 | 0 | (PR_StringToNetAddr(mHost.get(), &tempAddr) == PR_SUCCESS)) { // literal |
428 | 0 | LOG(("TRR::ReceivePush failed to decode %s\n", mHost.get())); |
429 | 0 | return NS_ERROR_UNEXPECTED; |
430 | 0 | } |
431 | 0 |
|
432 | 0 | RefPtr<nsHostRecord> hostRecord; |
433 | 0 | nsresult rv; |
434 | 0 | rv = mHostResolver->GetHostRecord(mHost, |
435 | 0 | pushedRec->flags, pushedRec->af, |
436 | 0 | pushedRec->pb, |
437 | 0 | pushedRec->originSuffix, |
438 | 0 | getter_AddRefs(hostRecord)); |
439 | 0 | if (NS_FAILED(rv)) { |
440 | 0 | return rv; |
441 | 0 | } |
442 | 0 | |
443 | 0 | rv = mHostResolver->TrrLookup_unlocked(hostRecord, this); |
444 | 0 | if (NS_FAILED(rv)) { |
445 | 0 | return rv; |
446 | 0 | } |
447 | 0 | |
448 | 0 | rv = pushed->AsyncOpen2(this); |
449 | 0 | if (NS_FAILED(rv)) { |
450 | 0 | return rv; |
451 | 0 | } |
452 | 0 | |
453 | 0 | // OK! |
454 | 0 | mChannel = pushed; |
455 | 0 | mRec.swap(hostRecord); |
456 | 0 |
|
457 | 0 | return NS_OK; |
458 | 0 | } |
459 | | |
460 | | NS_IMETHODIMP |
461 | | TRR::OnPush(nsIHttpChannel *associated, nsIHttpChannel *pushed) |
462 | 0 | { |
463 | 0 | LOG(("TRR::OnPush entry\n")); |
464 | 0 | MOZ_ASSERT(associated == mChannel); |
465 | 0 | if (!mRec) { |
466 | 0 | return NS_ERROR_FAILURE; |
467 | 0 | } |
468 | 0 | |
469 | 0 | RefPtr<TRR> trr = new TRR(mHostResolver, mPB); |
470 | 0 | return trr->ReceivePush(pushed, mRec); |
471 | 0 | } |
472 | | |
473 | | NS_IMETHODIMP |
474 | | TRR::OnStartRequest(nsIRequest *aRequest, |
475 | | nsISupports *aContext) |
476 | 0 | { |
477 | 0 | LOG(("TRR::OnStartRequest %p %s %d\n", this, mHost.get(), mType)); |
478 | 0 | mStartTime = TimeStamp::Now(); |
479 | 0 | return NS_OK; |
480 | 0 | } |
481 | | |
482 | | static uint16_t get16bit(unsigned char *aData, int index) |
483 | 0 | { |
484 | 0 | return ((aData[index] << 8) | aData[index + 1]); |
485 | 0 | } |
486 | | |
487 | | static uint32_t get32bit(unsigned char *aData, int index) |
488 | 0 | { |
489 | 0 | return (aData[index] << 24) | (aData[index+1] << 16) | |
490 | 0 | (aData[index+2] << 8) | aData[index+3]; |
491 | 0 | } |
492 | | |
493 | | nsresult |
494 | | TRR::PassQName(unsigned int &index) |
495 | 0 | { |
496 | 0 | uint8_t length; |
497 | 0 | do { |
498 | 0 | if (mBodySize < (index + 1)) { |
499 | 0 | LOG(("TRR: PassQName:%d fail at index %d\n", __LINE__, index)); |
500 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
501 | 0 | } |
502 | 0 | length = static_cast<uint8_t>(mResponse[index]); |
503 | 0 | if ((length & 0xc0) == 0xc0) { |
504 | 0 | // name pointer, advance over it and be done |
505 | 0 | if (mBodySize < (index + 2)) { |
506 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
507 | 0 | } |
508 | 0 | index += 2; |
509 | 0 | break; |
510 | 0 | } |
511 | 0 | if (length & 0xc0) { |
512 | 0 | LOG(("TRR: illegal label length byte (%x) at index %d\n", length, index)); |
513 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
514 | 0 | } |
515 | 0 | // pass label |
516 | 0 | if (mBodySize < (index + 1 + length)) { |
517 | 0 | LOG(("TRR: PassQName:%d fail at index %d\n", __LINE__, index)); |
518 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
519 | 0 | } |
520 | 0 | index += 1 + length; |
521 | 0 | } while (length); |
522 | 0 | return NS_OK; |
523 | 0 | } |
524 | | |
525 | | // GetQname: retrieves the qname (stores in 'aQname') and stores the index |
526 | | // after qname was parsed into the 'aIndex'. |
527 | | |
528 | | nsresult |
529 | | TRR::GetQname(nsAutoCString &aQname, unsigned int &aIndex) |
530 | 0 | { |
531 | 0 | uint8_t clength = 0; |
532 | 0 | unsigned int cindex = aIndex; |
533 | 0 | unsigned int loop = 128; // a valid DNS name can never loop this much |
534 | 0 | unsigned int endindex = 0; // index position after this data |
535 | 0 | do { |
536 | 0 | if (cindex >= mBodySize) { |
537 | 0 | LOG(("TRR: bad cname packet\n")); |
538 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
539 | 0 | } |
540 | 0 | clength = static_cast<uint8_t>(mResponse[cindex]); |
541 | 0 | if ((clength & 0xc0) == 0xc0) { |
542 | 0 | // name pointer, get the new offset (14 bits) |
543 | 0 | if ((cindex +1) >= mBodySize) { |
544 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
545 | 0 | } |
546 | 0 | // extract the new index position for the next label |
547 | 0 | uint16_t newpos = (clength & 0x3f) << 8 | mResponse[cindex+1]; |
548 | 0 | if (!endindex) { |
549 | 0 | // only update on the first "jump" |
550 | 0 | endindex = cindex + 2; |
551 | 0 | } |
552 | 0 | cindex = newpos; |
553 | 0 | continue; |
554 | 0 | } else if (clength & 0xc0) { |
555 | 0 | // any of those bits set individually is an error |
556 | 0 | LOG(("TRR: bad cname packet\n")); |
557 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
558 | 0 | } else { |
559 | 0 | cindex++; |
560 | 0 | } |
561 | 0 | if (clength) { |
562 | 0 | if (!aQname.IsEmpty()) { |
563 | 0 | aQname.Append("."); |
564 | 0 | } |
565 | 0 | if ((cindex + clength) > mBodySize) { |
566 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
567 | 0 | } |
568 | 0 | aQname.Append((const char *)(&mResponse[cindex]), clength); |
569 | 0 | cindex += clength; // skip label |
570 | 0 | } |
571 | 0 | } while (clength && --loop); |
572 | 0 |
|
573 | 0 | if (!loop) { |
574 | 0 | LOG(("TRR::DohDecode pointer loop error\n")); |
575 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
576 | 0 | } |
577 | 0 | if (!endindex) { |
578 | 0 | // there was no "jump" |
579 | 0 | endindex = cindex; |
580 | 0 | } |
581 | 0 | aIndex = endindex; |
582 | 0 | return NS_OK; |
583 | 0 | } |
584 | | |
585 | | // |
586 | | // DohDecode() collects the TTL and the IP addresses in the response |
587 | | // |
588 | | nsresult |
589 | | TRR::DohDecode(nsCString &aHost) |
590 | 0 | { |
591 | 0 | // The response has a 12 byte header followed by the question (returned) |
592 | 0 | // and then the answer. The answer section itself contains the name, type |
593 | 0 | // and class again and THEN the record data. |
594 | 0 |
|
595 | 0 | // www.example.com response: |
596 | 0 | // header: |
597 | 0 | // abcd 8180 0001 0001 0000 0000 |
598 | 0 | // the question: |
599 | 0 | // 0377 7777 0765 7861 6d70 6c65 0363 6f6d 0000 0100 01 |
600 | 0 | // the answer: |
601 | 0 | // 03 7777 7707 6578 616d 706c 6503 636f 6d00 0001 0001 |
602 | 0 | // 0000 0080 0004 5db8 d822 |
603 | 0 |
|
604 | 0 | unsigned int index = 12; |
605 | 0 | uint8_t length; |
606 | 0 | nsAutoCString host; |
607 | 0 | nsresult rv; |
608 | 0 |
|
609 | 0 | LOG(("doh decode %s %d bytes\n", aHost.get(), mBodySize)); |
610 | 0 |
|
611 | 0 | mCname.Truncate(); |
612 | 0 |
|
613 | 0 | if (mBodySize < 12 || mResponse[0] || mResponse[1]) { |
614 | 0 | LOG(("TRR bad incoming DOH, eject!\n")); |
615 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
616 | 0 | } |
617 | 0 | uint8_t rcode = mResponse[3] & 0x0F; |
618 | 0 | if (rcode) { |
619 | 0 | LOG(("TRR Decode %s RCODE %d\n", aHost.get(), rcode)); |
620 | 0 | return NS_ERROR_FAILURE; |
621 | 0 | } |
622 | 0 |
|
623 | 0 | uint16_t questionRecords = get16bit(mResponse, 4); // qdcount |
624 | 0 | // iterate over the single(?) host name in question |
625 | 0 | while (questionRecords) { |
626 | 0 | do { |
627 | 0 | if (mBodySize < (index + 1)) { |
628 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
629 | 0 | } |
630 | 0 | length = static_cast<uint8_t>(mResponse[index]); |
631 | 0 | if (length) { |
632 | 0 | if (host.Length()) { |
633 | 0 | host.Append("."); |
634 | 0 | } |
635 | 0 | if (mBodySize < (index + 1 + length)) { |
636 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
637 | 0 | } |
638 | 0 | host.Append(((char *)mResponse) + index + 1, length); |
639 | 0 | } |
640 | 0 | index += 1 + length; // skip length byte + label |
641 | 0 | } while (length); |
642 | 0 | if (mBodySize < (index + 4)) { |
643 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
644 | 0 | } |
645 | 0 | index += 4; // skip question's type, class |
646 | 0 | questionRecords--; |
647 | 0 | } |
648 | 0 |
|
649 | 0 | // Figure out the number of answer records from ANCOUNT |
650 | 0 | uint16_t answerRecords = get16bit(mResponse, 6); |
651 | 0 |
|
652 | 0 | LOG(("TRR Decode: %d answer records (%u bytes body) %s index=%u\n", |
653 | 0 | answerRecords, mBodySize, host.get(), index)); |
654 | 0 |
|
655 | 0 | while (answerRecords) { |
656 | 0 | nsAutoCString qname; |
657 | 0 | rv = GetQname(qname, index); |
658 | 0 | if (NS_FAILED(rv)) { |
659 | 0 | return rv; |
660 | 0 | } |
661 | 0 | // 16 bit TYPE |
662 | 0 | if (mBodySize < (index + 2)) { |
663 | 0 | LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index + 2)); |
664 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
665 | 0 | } |
666 | 0 | uint16_t TYPE = get16bit(mResponse, index); |
667 | 0 |
|
668 | 0 | if ((TYPE != TRRTYPE_CNAME) && |
669 | 0 | (TYPE != static_cast<uint16_t>(mType))) { |
670 | 0 | // Not the same type as was asked for nor CNAME |
671 | 0 | LOG(("TRR: Dohdecode:%d asked for type %d got %d\n", __LINE__, |
672 | 0 | mType, TYPE)); |
673 | 0 | return NS_ERROR_UNEXPECTED; |
674 | 0 | } |
675 | 0 | index += 2; |
676 | 0 |
|
677 | 0 | // 16 bit class |
678 | 0 | if (mBodySize < (index + 2)) { |
679 | 0 | LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index + 2)); |
680 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
681 | 0 | } |
682 | 0 | uint16_t CLASS = get16bit(mResponse, index); |
683 | 0 | if (kDNS_CLASS_IN != CLASS) { |
684 | 0 | LOG(("TRR bad CLASS (%u) at index %d\n", CLASS, index)); |
685 | 0 | return NS_ERROR_UNEXPECTED; |
686 | 0 | } |
687 | 0 | index += 2; |
688 | 0 |
|
689 | 0 | // 32 bit TTL (seconds) |
690 | 0 | if (mBodySize < (index + 4)) { |
691 | 0 | LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index)); |
692 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
693 | 0 | } |
694 | 0 | uint32_t TTL = get32bit(mResponse, index); |
695 | 0 | index += 4; |
696 | 0 |
|
697 | 0 | // 16 bit RDLENGTH |
698 | 0 | if (mBodySize < (index + 2)) { |
699 | 0 | LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index)); |
700 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
701 | 0 | } |
702 | 0 | uint16_t RDLENGTH = get16bit(mResponse, index); |
703 | 0 | index += 2; |
704 | 0 |
|
705 | 0 | if (mBodySize < (index + RDLENGTH)) { |
706 | 0 | LOG(("TRR: Dohdecode:%d fail RDLENGTH=%d at index %d\n", __LINE__, |
707 | 0 | RDLENGTH, index)); |
708 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
709 | 0 | } |
710 | 0 |
|
711 | 0 | if (qname.Equals(aHost)) { |
712 | 0 |
|
713 | 0 | // RDATA |
714 | 0 | // - A (TYPE 1): 4 bytes |
715 | 0 | // - AAAA (TYPE 28): 16 bytes |
716 | 0 | // - NS (TYPE 2): N bytes |
717 | 0 |
|
718 | 0 | switch(TYPE) { |
719 | 0 | case TRRTYPE_A: |
720 | 0 | if (RDLENGTH != 4) { |
721 | 0 | LOG(("TRR bad length for A (%u)\n", RDLENGTH)); |
722 | 0 | return NS_ERROR_UNEXPECTED; |
723 | 0 | } |
724 | 0 | rv = mDNS.Add(TTL, mResponse, index, RDLENGTH, |
725 | 0 | mAllowRFC1918); |
726 | 0 | if (NS_FAILED(rv)) { |
727 | 0 | LOG(("TRR:DohDecode failed: local IP addresses or unknown IP family\n")); |
728 | 0 | return rv; |
729 | 0 | } |
730 | 0 | break; |
731 | 0 | case TRRTYPE_AAAA: |
732 | 0 | if (RDLENGTH != 16) { |
733 | 0 | LOG(("TRR bad length for AAAA (%u)\n", RDLENGTH)); |
734 | 0 | return NS_ERROR_UNEXPECTED; |
735 | 0 | } |
736 | 0 | rv = mDNS.Add(TTL, mResponse, index, RDLENGTH, |
737 | 0 | mAllowRFC1918); |
738 | 0 | if (NS_FAILED(rv)) { |
739 | 0 | LOG(("TRR got unique/local IPv6 address!\n")); |
740 | 0 | return rv; |
741 | 0 | } |
742 | 0 | break; |
743 | 0 |
|
744 | 0 | case TRRTYPE_NS: |
745 | 0 | break; |
746 | 0 | case TRRTYPE_CNAME: |
747 | 0 | if (mCname.IsEmpty()) { |
748 | 0 | nsAutoCString qname; |
749 | 0 | unsigned int qnameindex = index; |
750 | 0 | rv = GetQname(qname, qnameindex); |
751 | 0 | if (NS_FAILED(rv)) { |
752 | 0 | return rv; |
753 | 0 | } |
754 | 0 | if(!qname.IsEmpty()) { |
755 | 0 | mCname = qname; |
756 | 0 | LOG(("TRR::DohDecode CNAME host %s => %s\n", |
757 | 0 | host.get(), mCname.get())); |
758 | 0 | } else { |
759 | 0 | LOG(("TRR::DohDecode empty CNAME for host %s!\n", |
760 | 0 | host.get())); |
761 | 0 | } |
762 | 0 | } |
763 | 0 | else { |
764 | 0 | LOG(("TRR::DohDecode CNAME - ignoring another entry\n")); |
765 | 0 | } |
766 | 0 | break; |
767 | 0 | case TRRTYPE_TXT: |
768 | 0 | { |
769 | 0 | // TXT record RRDATA sections are a series of character-strings |
770 | 0 | // each character string is a length byte followed by that many data bytes |
771 | 0 | nsAutoCString txt; |
772 | 0 | unsigned int txtIndex = index; |
773 | 0 | uint16_t available = RDLENGTH; |
774 | 0 |
|
775 | 0 | while (available > 0) { |
776 | 0 | uint8_t characterStringLen = mResponse[txtIndex++]; |
777 | 0 | available--; |
778 | 0 | if (characterStringLen > available) { |
779 | 0 | LOG(("TRR::DohDecode MALFORMED TXT RECORD\n")); |
780 | 0 | break; |
781 | 0 | } |
782 | 0 | txt.Append((const char *)(&mResponse[txtIndex]), characterStringLen); |
783 | 0 | txtIndex += characterStringLen; |
784 | 0 | available -= characterStringLen; |
785 | 0 | } |
786 | 0 |
|
787 | 0 | mTxt.AppendElement(txt); |
788 | 0 | if (mTxtTtl > TTL) { |
789 | 0 | mTxtTtl = TTL; |
790 | 0 | } |
791 | 0 | LOG(("TRR::DohDecode TXT host %s => %s\n", |
792 | 0 | host.get(), txt.get())); |
793 | 0 | break; |
794 | 0 | } |
795 | 0 | default: |
796 | 0 | // skip unknown record types |
797 | 0 | LOG(("TRR unsupported TYPE (%u) RDLENGTH %u\n", TYPE, RDLENGTH)); |
798 | 0 | break; |
799 | 0 | } |
800 | 0 | } |
801 | 0 | else { |
802 | 0 | LOG(("TRR asked for %s data but got %s\n", aHost.get(), qname.get())); |
803 | 0 | } |
804 | 0 |
|
805 | 0 | index += RDLENGTH; |
806 | 0 | LOG(("done with record type %u len %u index now %u of %u\n", |
807 | 0 | TYPE, RDLENGTH, index, mBodySize)); |
808 | 0 | answerRecords--; |
809 | 0 | } |
810 | 0 |
|
811 | 0 | // NSCOUNT |
812 | 0 | uint16_t nsRecords = get16bit(mResponse, 8); |
813 | 0 | LOG(("TRR Decode: %d ns records (%u bytes body)\n", nsRecords, mBodySize)); |
814 | 0 | while (nsRecords) { |
815 | 0 | rv = PassQName(index); |
816 | 0 | if (NS_FAILED(rv)) { |
817 | 0 | return rv; |
818 | 0 | } |
819 | 0 | |
820 | 0 | if (mBodySize < (index + 8)) { |
821 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
822 | 0 | } |
823 | 0 | index += 2; // type |
824 | 0 | index += 2; // class |
825 | 0 | index += 4; // ttl |
826 | 0 |
|
827 | 0 | // 16 bit RDLENGTH |
828 | 0 | if (mBodySize < (index + 2)) { |
829 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
830 | 0 | } |
831 | 0 | uint16_t RDLENGTH = get16bit(mResponse, index); |
832 | 0 | index += 2; |
833 | 0 | if (mBodySize < (index + RDLENGTH)) { |
834 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
835 | 0 | } |
836 | 0 | index += RDLENGTH; |
837 | 0 | LOG(("done with nsRecord now %u of %u\n", index, mBodySize)); |
838 | 0 | nsRecords--; |
839 | 0 | } |
840 | 0 |
|
841 | 0 | // additional resource records |
842 | 0 | uint16_t arRecords = get16bit(mResponse, 10); |
843 | 0 | LOG(("TRR Decode: %d additional resource records (%u bytes body)\n", |
844 | 0 | arRecords, mBodySize)); |
845 | 0 | while (arRecords) { |
846 | 0 | rv = PassQName(index); |
847 | 0 | if (NS_FAILED(rv)) { |
848 | 0 | return rv; |
849 | 0 | } |
850 | 0 | |
851 | 0 | if (mBodySize < (index + 8)) { |
852 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
853 | 0 | } |
854 | 0 | index += 2; // type |
855 | 0 | index += 2; // class |
856 | 0 | index += 4; // ttl |
857 | 0 |
|
858 | 0 | // 16 bit RDLENGTH |
859 | 0 | if (mBodySize < (index + 2)) { |
860 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
861 | 0 | } |
862 | 0 | uint16_t RDLENGTH = get16bit(mResponse, index); |
863 | 0 | index += 2; |
864 | 0 | if (mBodySize < (index + RDLENGTH)) { |
865 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
866 | 0 | } |
867 | 0 | index += RDLENGTH; |
868 | 0 | LOG(("done with additional rr now %u of %u\n", index, mBodySize)); |
869 | 0 | arRecords--; |
870 | 0 | } |
871 | 0 |
|
872 | 0 | if (index != mBodySize) { |
873 | 0 | LOG(("DohDecode failed to parse entire response body, %u out of %u bytes\n", |
874 | 0 | index, mBodySize)); |
875 | 0 | // failed to parse 100%, do not continue |
876 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
877 | 0 | } |
878 | 0 |
|
879 | 0 | if ((mType != TRRTYPE_NS) && mCname.IsEmpty() && |
880 | 0 | !mDNS.mAddresses.getFirst() && |
881 | 0 | mTxt.IsEmpty()) { |
882 | 0 | // no entries were stored! |
883 | 0 | LOG(("TRR: No entries were stored!\n")); |
884 | 0 | return NS_ERROR_FAILURE; |
885 | 0 | } |
886 | 0 | return NS_OK; |
887 | 0 | } |
888 | | |
889 | | nsresult |
890 | | TRR::ReturnData() |
891 | 0 | { |
892 | 0 | if (mType != TRRTYPE_TXT) { |
893 | 0 | // create and populate an AddrInfo instance to pass on |
894 | 0 | nsAutoPtr<AddrInfo> ai(new AddrInfo(mHost, mType)); |
895 | 0 | DOHaddr *item; |
896 | 0 | uint32_t ttl = AddrInfo::NO_TTL_DATA; |
897 | 0 | while ((item = static_cast<DOHaddr*>(mDNS.mAddresses.popFirst()))) { |
898 | 0 | PRNetAddr prAddr; |
899 | 0 | NetAddrToPRNetAddr(&item->mNet, &prAddr); |
900 | 0 | auto *addrElement = new NetAddrElement(&prAddr); |
901 | 0 | ai->AddAddress(addrElement); |
902 | 0 | if (item->mTtl < ttl) { |
903 | 0 | // While the DNS packet might return individual TTLs for each address, |
904 | 0 | // we can only return one value in the AddrInfo class so pick the |
905 | 0 | // lowest number. |
906 | 0 | ttl = item->mTtl; |
907 | 0 | } |
908 | 0 | } |
909 | 0 | ai->ttl = ttl; |
910 | 0 | if (!mHostResolver) { |
911 | 0 | return NS_ERROR_FAILURE; |
912 | 0 | } |
913 | 0 | (void)mHostResolver->CompleteLookup(mRec, NS_OK, ai.forget(), mPB); |
914 | 0 | mHostResolver = nullptr; |
915 | 0 | mRec = nullptr; |
916 | 0 | } else { |
917 | 0 | (void)mHostResolver->CompleteLookupByType(mRec, NS_OK, &mTxt, mTxtTtl, mPB); |
918 | 0 | } |
919 | 0 | return NS_OK; |
920 | 0 | } |
921 | | |
922 | | nsresult |
923 | | TRR::FailData(nsresult error) |
924 | 0 | { |
925 | 0 | if (!mHostResolver) { |
926 | 0 | return NS_ERROR_FAILURE; |
927 | 0 | } |
928 | 0 | |
929 | 0 | if (mType == TRRTYPE_TXT) { |
930 | 0 | (void)mHostResolver->CompleteLookupByType(mRec, error, |
931 | 0 | nullptr, 0, mPB); |
932 | 0 | } else { |
933 | 0 | // create and populate an TRR AddrInfo instance to pass on to signal that |
934 | 0 | // this comes from TRR |
935 | 0 | AddrInfo *ai = new AddrInfo(mHost, mType); |
936 | 0 |
|
937 | 0 | (void)mHostResolver->CompleteLookup(mRec, error, ai, mPB); |
938 | 0 | } |
939 | 0 |
|
940 | 0 | mHostResolver = nullptr; |
941 | 0 | mRec = nullptr; |
942 | 0 | return NS_OK; |
943 | 0 | } |
944 | | |
945 | | nsresult |
946 | | TRR::On200Response() |
947 | 0 | { |
948 | 0 | // decode body and create an AddrInfo struct for the response |
949 | 0 | nsresult rv = DohDecode(mHost); |
950 | 0 |
|
951 | 0 | if (NS_SUCCEEDED(rv)) { |
952 | 0 | if (!mDNS.mAddresses.getFirst() && !mCname.IsEmpty() && |
953 | 0 | mType != TRRTYPE_TXT) { |
954 | 0 | nsCString cname = mCname; |
955 | 0 | LOG(("TRR: check for CNAME record for %s within previous response\n", |
956 | 0 | cname.get())); |
957 | 0 | rv = DohDecode(cname); |
958 | 0 | if (NS_SUCCEEDED(rv) && mDNS.mAddresses.getFirst()) { |
959 | 0 | LOG(("TRR: Got the CNAME record without asking for it\n")); |
960 | 0 | ReturnData(); |
961 | 0 | return NS_OK; |
962 | 0 | } |
963 | 0 | // restore mCname as DohDecode() change it |
964 | 0 | mCname = cname; |
965 | 0 | if (!--mCnameLoop) { |
966 | 0 | LOG(("TRR::On200Response CNAME loop, eject!\n")); |
967 | 0 | } else { |
968 | 0 | LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(), |
969 | 0 | mCnameLoop)); |
970 | 0 | RefPtr<TRR> trr = new TRR(mHostResolver, mRec, mCname, |
971 | 0 | mType, mCnameLoop, mPB); |
972 | 0 | rv = NS_DispatchToMainThread(trr); |
973 | 0 | if (NS_SUCCEEDED(rv)) { |
974 | 0 | return rv; |
975 | 0 | } |
976 | 0 | } |
977 | 0 | } else { |
978 | 0 | // pass back the response data |
979 | 0 | ReturnData(); |
980 | 0 | return NS_OK; |
981 | 0 | } |
982 | 0 | } else { |
983 | 0 | LOG(("TRR::On200Response DohDecode %x\n", (int)rv)); |
984 | 0 | } |
985 | 0 | return NS_ERROR_FAILURE; |
986 | 0 | } |
987 | | |
988 | | |
989 | | NS_IMETHODIMP |
990 | | TRR::OnStopRequest(nsIRequest *aRequest, |
991 | | nsISupports *aContext, |
992 | | nsresult aStatusCode) |
993 | 0 | { |
994 | 0 | // The dtor will be run after the function returns |
995 | 0 | LOG(("TRR:OnStopRequest %p %s %d failed=%d code=%X\n", |
996 | 0 | this, mHost.get(), mType, mFailed, (unsigned int)aStatusCode)); |
997 | 0 | nsCOMPtr<nsIChannel> channel; |
998 | 0 | channel.swap(mChannel); |
999 | 0 |
|
1000 | 0 | // if status was "fine", parse the response and pass on the answer |
1001 | 0 | if (!mFailed && NS_SUCCEEDED(aStatusCode)) { |
1002 | 0 | nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest); |
1003 | 0 | if (!httpChannel) { |
1004 | 0 | return NS_ERROR_UNEXPECTED; |
1005 | 0 | } |
1006 | 0 | nsresult rv = NS_OK; |
1007 | 0 | nsAutoCString contentType; |
1008 | 0 | httpChannel->GetContentType(contentType); |
1009 | 0 | if (contentType.Length() && |
1010 | 0 | !contentType.LowerCaseEqualsLiteral("application/dns-message")) { |
1011 | 0 | LOG(("TRR:OnStopRequest %p %s %d wrong content type %s\n", |
1012 | 0 | this, mHost.get(), mType, contentType.get())); |
1013 | 0 | FailData(NS_ERROR_UNEXPECTED); |
1014 | 0 | return NS_OK; |
1015 | 0 | } |
1016 | 0 |
|
1017 | 0 | uint32_t httpStatus; |
1018 | 0 | rv = httpChannel->GetResponseStatus(&httpStatus); |
1019 | 0 | if (NS_SUCCEEDED(rv) && httpStatus == 200) { |
1020 | 0 | rv = On200Response(); |
1021 | 0 | if (NS_SUCCEEDED(rv)) { |
1022 | 0 | return rv; |
1023 | 0 | } |
1024 | 0 | } else { |
1025 | 0 | LOG(("TRR:OnStopRequest:%d %p rv %x httpStatus %d\n", __LINE__, |
1026 | 0 | this, (int)rv, httpStatus)); |
1027 | 0 | } |
1028 | 0 | } |
1029 | 0 |
|
1030 | 0 | LOG(("TRR:OnStopRequest %p status %x mFailed %d\n", |
1031 | 0 | this, (int)aStatusCode, mFailed)); |
1032 | 0 | FailData(NS_ERROR_UNKNOWN_HOST); |
1033 | 0 | return NS_OK; |
1034 | 0 | } |
1035 | | |
1036 | | NS_IMETHODIMP |
1037 | | TRR::OnDataAvailable(nsIRequest *aRequest, |
1038 | | nsISupports *aContext, |
1039 | | nsIInputStream *aInputStream, |
1040 | | uint64_t aOffset, |
1041 | | const uint32_t aCount) |
1042 | 0 | { |
1043 | 0 | LOG(("TRR:OnDataAvailable %p %s %d failed=%d aCount=%u\n", |
1044 | 0 | this, mHost.get(), mType, mFailed, (unsigned int)aCount)); |
1045 | 0 | // receive DNS response into the local buffer |
1046 | 0 | if (mFailed) { |
1047 | 0 | return NS_ERROR_FAILURE; |
1048 | 0 | } |
1049 | 0 | |
1050 | 0 | if (aCount + mBodySize > kMaxSize) { |
1051 | 0 | LOG(("TRR::OnDataAvailable:%d fail\n", __LINE__)); |
1052 | 0 | mFailed = true; |
1053 | 0 | return NS_ERROR_FAILURE; |
1054 | 0 | } |
1055 | 0 |
|
1056 | 0 | uint32_t count; |
1057 | 0 | nsresult rv = aInputStream->Read((char *)mResponse + mBodySize, aCount, &count); |
1058 | 0 | if (NS_FAILED(rv)) { |
1059 | 0 | LOG(("TRR::OnDataAvailable:%d fail\n", __LINE__)); |
1060 | 0 | mFailed = true; |
1061 | 0 | return rv; |
1062 | 0 | } |
1063 | 0 | MOZ_ASSERT(count == aCount); |
1064 | 0 | mBodySize += aCount; |
1065 | 0 | return NS_OK; |
1066 | 0 | } |
1067 | | |
1068 | | nsresult |
1069 | | DOHresp::Add(uint32_t TTL, unsigned char *dns, int index, uint16_t len, |
1070 | | bool aLocalAllowed) |
1071 | 0 | { |
1072 | 0 | nsAutoPtr<DOHaddr> doh(new DOHaddr); |
1073 | 0 | NetAddr *addr = &doh->mNet; |
1074 | 0 | if (4 == len) { |
1075 | 0 | // IPv4 |
1076 | 0 | addr->inet.family = AF_INET; |
1077 | 0 | addr->inet.port = 0; // unknown |
1078 | 0 | addr->inet.ip = ntohl(get32bit(dns, index)); |
1079 | 0 | } else if (16 == len) { |
1080 | 0 | // IPv6 |
1081 | 0 | addr->inet6.family = AF_INET6; |
1082 | 0 | addr->inet6.port = 0; // unknown |
1083 | 0 | addr->inet6.flowinfo = 0; // unknown |
1084 | 0 | addr->inet6.scope_id = 0; // unknown |
1085 | 0 | for(int i = 0; i < 16; i++, index++) { |
1086 | 0 | addr->inet6.ip.u8[i] = dns[index]; |
1087 | 0 | } |
1088 | 0 | } else { |
1089 | 0 | return NS_ERROR_UNEXPECTED; |
1090 | 0 | } |
1091 | 0 | |
1092 | 0 | if (IsIPAddrLocal(addr) && !aLocalAllowed) { |
1093 | 0 | return NS_ERROR_FAILURE; |
1094 | 0 | } |
1095 | 0 | doh->mTtl = TTL; |
1096 | 0 |
|
1097 | 0 | if (LOG_ENABLED()) { |
1098 | 0 | char buf[128]; |
1099 | 0 | NetAddrToString(addr, buf, sizeof(buf)); |
1100 | 0 | LOG(("DOHresp:Add %s\n", buf)); |
1101 | 0 | } |
1102 | 0 | mAddresses.insertBack(doh.forget()); |
1103 | 0 | return NS_OK; |
1104 | 0 | } |
1105 | | |
1106 | | class ProxyCancel : public Runnable |
1107 | | { |
1108 | | public: |
1109 | | explicit ProxyCancel(TRR *aTRR) |
1110 | | : Runnable("proxyTrrCancel") |
1111 | | , mTRR(aTRR) |
1112 | 0 | { } |
1113 | | |
1114 | | NS_IMETHOD Run() override |
1115 | 0 | { |
1116 | 0 | mTRR->Cancel(); |
1117 | 0 | mTRR = nullptr; |
1118 | 0 | return NS_OK; |
1119 | 0 | } |
1120 | | |
1121 | | private: |
1122 | | RefPtr<TRR> mTRR; |
1123 | | }; |
1124 | | |
1125 | | void |
1126 | | TRR::Cancel() |
1127 | 0 | { |
1128 | 0 | if (!NS_IsMainThread()) { |
1129 | 0 | NS_DispatchToMainThread(new ProxyCancel(this)); |
1130 | 0 | return; |
1131 | 0 | } |
1132 | 0 | if (mChannel) { |
1133 | 0 | LOG(("TRR: %p canceling Channel %p %s %d\n", this, |
1134 | 0 | mChannel.get(), mHost.get(), mType)); |
1135 | 0 | mChannel->Cancel(NS_ERROR_ABORT); |
1136 | 0 | } |
1137 | 0 | } |
1138 | | |
1139 | | #undef LOG |
1140 | | |
1141 | | // namespace |
1142 | | } |
1143 | | } |