blob: 7610c92033a676fa18a33571dc24b0de4794a3ff [file] [log] [blame]
Ben Schwartze7601812017-04-28 16:38:29 -04001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "dns/DnsTlsTransport.h"
18
19#include <arpa/inet.h>
20#include <arpa/nameser.h>
21#include <errno.h>
22#include <openssl/err.h>
Ben Schwartze7601812017-04-28 16:38:29 -040023
24#define LOG_TAG "DnsTlsTransport"
25#define DBG 0
26
27#include "log/log.h"
28#include "Fwmark.h"
29#undef ADD // already defined in nameser.h
30#include "NetdConstants.h"
31#include "Permission.h"
32
33
34namespace android {
35namespace net {
36
37namespace {
38
Erik Kline51b7c482018-01-12 17:47:45 +090039constexpr const char kCaCertDir[] = "/system/etc/security/cacerts";
40
Ben Schwartze7601812017-04-28 16:38:29 -040041bool setNonBlocking(int fd, bool enabled) {
42 int flags = fcntl(fd, F_GETFL);
43 if (flags < 0) return false;
44
45 if (enabled) {
46 flags |= O_NONBLOCK;
47 } else {
48 flags &= ~O_NONBLOCK;
49 }
50 return (fcntl(fd, F_SETFL, flags) == 0);
51}
52
53int waitForReading(int fd) {
54 fd_set fds;
55 FD_ZERO(&fds);
56 FD_SET(fd, &fds);
57 const int ret = TEMP_FAILURE_RETRY(select(fd + 1, &fds, nullptr, nullptr, nullptr));
58 if (DBG && ret <= 0) {
59 ALOGD("select");
60 }
61 return ret;
62}
63
64int waitForWriting(int fd) {
65 fd_set fds;
66 FD_ZERO(&fds);
67 FD_SET(fd, &fds);
68 const int ret = TEMP_FAILURE_RETRY(select(fd + 1, nullptr, &fds, nullptr, nullptr));
69 if (DBG && ret <= 0) {
70 ALOGD("select");
71 }
72 return ret;
73}
74
75} // namespace
76
77android::base::unique_fd DnsTlsTransport::makeConnectedSocket() const {
Ben Schwartza13c23a2017-10-02 12:06:21 -040078 if (DBG) {
79 ALOGD("%u connecting TCP socket", mMark);
80 }
Ben Schwartze7601812017-04-28 16:38:29 -040081 android::base::unique_fd fd;
82 int type = SOCK_NONBLOCK | SOCK_CLOEXEC;
Ben Schwartz52504622017-07-11 12:21:13 -040083 switch (mServer.protocol) {
Ben Schwartze7601812017-04-28 16:38:29 -040084 case IPPROTO_TCP:
85 type |= SOCK_STREAM;
86 break;
87 default:
88 errno = EPROTONOSUPPORT;
89 return fd;
90 }
91
Ben Schwartz52504622017-07-11 12:21:13 -040092 fd.reset(socket(mServer.ss.ss_family, type, mServer.protocol));
Ben Schwartze7601812017-04-28 16:38:29 -040093 if (fd.get() == -1) {
94 return fd;
95 }
96
97 const socklen_t len = sizeof(mMark);
98 if (setsockopt(fd.get(), SOL_SOCKET, SO_MARK, &mMark, len) == -1) {
99 fd.reset();
100 } else if (connect(fd.get(),
Ben Schwartz52504622017-07-11 12:21:13 -0400101 reinterpret_cast<const struct sockaddr *>(&mServer.ss), sizeof(mServer.ss)) != 0
Ben Schwartze7601812017-04-28 16:38:29 -0400102 && errno != EINPROGRESS) {
103 fd.reset();
104 }
105
Ben Schwartza13c23a2017-10-02 12:06:21 -0400106 if (!setNonBlocking(fd, false)) {
107 ALOGE("Failed to disable nonblocking status on DNS-over-TLS fd");
108 fd.reset();
109 }
110
Ben Schwartze7601812017-04-28 16:38:29 -0400111 return fd;
112}
113
114bool getSPKIDigest(const X509* cert, std::vector<uint8_t>* out) {
115 int spki_len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), NULL);
116 unsigned char spki[spki_len];
117 unsigned char* temp = spki;
118 if (spki_len != i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &temp)) {
119 ALOGW("SPKI length mismatch");
120 return false;
121 }
122 out->resize(SHA256_SIZE);
123 unsigned int digest_len = 0;
124 int ret = EVP_Digest(spki, spki_len, out->data(), &digest_len, EVP_sha256(), NULL);
125 if (ret != 1) {
126 ALOGW("Server cert digest extraction failed");
127 return false;
128 }
129 if (digest_len != out->size()) {
130 ALOGW("Wrong digest length: %d", digest_len);
131 return false;
132 }
133 return true;
134}
135
Ben Schwartza13c23a2017-10-02 12:06:21 -0400136bool DnsTlsTransport::initialize() {
Ben Schwartz66810f62017-10-16 19:27:46 -0400137 // This method should only be called once, at the beginning, so locking should be
138 // unnecessary. This lock only serves to help catch bugs in code that calls this method.
139 std::lock_guard<std::mutex> guard(mLock);
140 if (mSslCtx) {
141 // This is a bug in the caller.
142 return false;
143 }
Ben Schwartza13c23a2017-10-02 12:06:21 -0400144 mSslCtx.reset(SSL_CTX_new(TLS_method()));
145 if (!mSslCtx) {
146 return false;
147 }
Erik Kline51b7c482018-01-12 17:47:45 +0900148
149 // Load system CA certs for hostname verification.
150 //
151 // For discussion of alternative, sustainable approaches see b/71909242.
152 if (SSL_CTX_load_verify_locations(mSslCtx.get(), nullptr, kCaCertDir) != 1) {
153 ALOGE("Failed to load CA cert dir: %s", kCaCertDir);
154 return false;
155 }
156
Ben Schwartza13c23a2017-10-02 12:06:21 -0400157 SSL_CTX_sess_set_new_cb(mSslCtx.get(), DnsTlsTransport::newSessionCallback);
158 SSL_CTX_sess_set_remove_cb(mSslCtx.get(), DnsTlsTransport::removeSessionCallback);
dalyk0ae643f2017-12-08 16:22:55 -0500159
160 // Enable TLS false start.
161 SSL_CTX_set_false_start_allowed_without_alpn(mSslCtx.get(), 1);
162 SSL_CTX_set_mode(mSslCtx.get(), SSL_MODE_ENABLE_FALSE_START);
Ben Schwartza13c23a2017-10-02 12:06:21 -0400163 return true;
164}
165
166bssl::UniquePtr<SSL> DnsTlsTransport::sslConnect(int fd) {
167 // Check TLS context.
168 if (!mSslCtx) {
169 ALOGE("Internal error: context is null in ssl connect");
170 return nullptr;
171 }
172 if (!SSL_CTX_set_max_proto_version(mSslCtx.get(), TLS1_3_VERSION) ||
173 !SSL_CTX_set_min_proto_version(mSslCtx.get(), TLS1_2_VERSION)) {
174 ALOGE("failed to min/max TLS versions");
Ben Schwartze7601812017-04-28 16:38:29 -0400175 return nullptr;
176 }
177
Ben Schwartza13c23a2017-10-02 12:06:21 -0400178 bssl::UniquePtr<SSL> ssl(SSL_new(mSslCtx.get()));
Ben Schwartz4204ecf2017-10-02 12:35:48 -0400179 // This file descriptor is owned by a unique_fd, so don't let libssl close it.
180 bssl::UniquePtr<BIO> bio(BIO_new_socket(fd, BIO_NOCLOSE));
Ben Schwartze7601812017-04-28 16:38:29 -0400181 SSL_set_bio(ssl.get(), bio.get(), bio.get());
182 bio.release();
183
Ben Schwartza13c23a2017-10-02 12:06:21 -0400184 // Add this transport as the 0-index extra data for the socket.
185 // This is used by newSessionCallback.
186 if (SSL_set_ex_data(ssl.get(), 0, this) != 1) {
187 ALOGE("failed to associate SSL socket to transport");
188 return nullptr;
189 }
190
191 // Add this transport as the 0-index extra data for the context.
192 // This is used by removeSessionCallback.
193 if (SSL_CTX_set_ex_data(mSslCtx.get(), 0, this) != 1) {
194 ALOGE("failed to associate SSL context to transport");
Ben Schwartze7601812017-04-28 16:38:29 -0400195 return nullptr;
196 }
197
Ben Schwartz1691bc42017-08-16 12:53:09 -0400198 if (!mServer.name.empty()) {
199 if (SSL_set_tlsext_host_name(ssl.get(), mServer.name.c_str()) != 1) {
200 ALOGE("Failed to set SNI to %s", mServer.name.c_str());
201 return nullptr;
202 }
203 X509_VERIFY_PARAM* param = SSL_get0_param(ssl.get());
204 X509_VERIFY_PARAM_set1_host(param, mServer.name.c_str(), 0);
205 // This will cause the handshake to fail if certificate verification fails.
206 SSL_set_verify(ssl.get(), SSL_VERIFY_PEER, nullptr);
207 }
208
Ben Schwartza13c23a2017-10-02 12:06:21 -0400209 bssl::UniquePtr<SSL_SESSION> session;
210 {
Ben Schwartz66810f62017-10-16 19:27:46 -0400211 std::lock_guard<std::mutex> guard(mLock);
Ben Schwartza13c23a2017-10-02 12:06:21 -0400212 if (!mSessions.empty()) {
213 session = std::move(mSessions.front());
214 mSessions.pop_front();
215 } else if (DBG) {
216 ALOGD("Starting without session ticket.");
217 }
218 }
219 if (session) {
220 SSL_set_session(ssl.get(), session.get());
221 }
222
Ben Schwartze7601812017-04-28 16:38:29 -0400223 for (;;) {
224 if (DBG) {
225 ALOGD("%u Calling SSL_connect", mMark);
226 }
227 int ret = SSL_connect(ssl.get());
228 if (DBG) {
229 ALOGD("%u SSL_connect returned %d", mMark, ret);
230 }
231 if (ret == 1) break; // SSL handshake complete;
232
233 const int ssl_err = SSL_get_error(ssl.get(), ret);
234 switch (ssl_err) {
235 case SSL_ERROR_WANT_READ:
236 if (waitForReading(fd) != 1) {
237 ALOGW("SSL_connect read error");
238 return nullptr;
239 }
240 break;
241 case SSL_ERROR_WANT_WRITE:
242 if (waitForWriting(fd) != 1) {
243 ALOGW("SSL_connect write error");
244 return nullptr;
245 }
246 break;
247 default:
248 ALOGW("SSL_connect error %d, errno=%d", ssl_err, errno);
249 return nullptr;
250 }
251 }
252
Ben Schwartz52504622017-07-11 12:21:13 -0400253 if (!mServer.fingerprints.empty()) {
Ben Schwartze7601812017-04-28 16:38:29 -0400254 if (DBG) {
255 ALOGD("Checking DNS over TLS fingerprint");
256 }
Ben Schwartzf028d392017-07-10 15:07:12 -0400257
258 // We only care that the chain is internally self-consistent, not that
259 // it chains to a trusted root, so we can ignore some kinds of errors.
260 // TODO: Add a CA root verification mode that respects these errors.
261 int verify_result = SSL_get_verify_result(ssl.get());
262 switch (verify_result) {
263 case X509_V_OK:
264 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
265 case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
266 case X509_V_ERR_CERT_UNTRUSTED:
267 break;
268 default:
269 ALOGW("Invalid certificate chain, error %d", verify_result);
270 return nullptr;
271 }
272
273 STACK_OF(X509) *chain = SSL_get_peer_cert_chain(ssl.get());
274 if (!chain) {
Ben Schwartze7601812017-04-28 16:38:29 -0400275 ALOGW("Server has null certificate");
276 return nullptr;
277 }
Ben Schwartzf028d392017-07-10 15:07:12 -0400278 // Chain and its contents are owned by ssl, so we don't need to free explicitly.
279 bool matched = false;
280 for (size_t i = 0; i < sk_X509_num(chain); ++i) {
281 // This appears to be O(N^2), but there doesn't seem to be a straightforward
282 // way to walk a STACK_OF nondestructively in linear time.
283 X509* cert = sk_X509_value(chain, i);
284 std::vector<uint8_t> digest;
285 if (!getSPKIDigest(cert, &digest)) {
286 ALOGE("Digest computation failed");
287 return nullptr;
288 }
289
290 if (mServer.fingerprints.count(digest) > 0) {
291 matched = true;
292 break;
293 }
Ben Schwartze7601812017-04-28 16:38:29 -0400294 }
295
Ben Schwartzf028d392017-07-10 15:07:12 -0400296 if (!matched) {
Ben Schwartze7601812017-04-28 16:38:29 -0400297 ALOGW("No matching fingerprint");
298 return nullptr;
299 }
Ben Schwartzf028d392017-07-10 15:07:12 -0400300
Ben Schwartze7601812017-04-28 16:38:29 -0400301 if (DBG) {
302 ALOGD("DNS over TLS fingerprint is correct");
303 }
304 }
305
306 if (DBG) {
307 ALOGD("%u handshake complete", mMark);
308 }
Ben Schwartza13c23a2017-10-02 12:06:21 -0400309
310 return ssl;
311}
312
313// static
314int DnsTlsTransport::newSessionCallback(SSL* ssl, SSL_SESSION* session) {
315 if (!session) {
316 return 0;
317 }
318 if (DBG) {
319 ALOGD("Recording session ticket");
320 }
321 DnsTlsTransport* xport = reinterpret_cast<DnsTlsTransport*>(
322 SSL_get_ex_data(ssl, 0));
323 if (!xport) {
324 ALOGE("null transport in new session callback");
325 return 0;
326 }
327 xport->recordSession(session);
328 return 1;
329}
330
331void DnsTlsTransport::removeSessionCallback(SSL_CTX* ssl_ctx, SSL_SESSION* session) {
332 if (DBG) {
333 ALOGD("Removing session ticket");
334 }
335 DnsTlsTransport* xport = reinterpret_cast<DnsTlsTransport*>(
336 SSL_CTX_get_ex_data(ssl_ctx, 0));
337 if (!xport) {
338 ALOGE("null transport in remove session callback");
339 return;
340 }
341 xport->removeSession(session);
342}
343
344void DnsTlsTransport::recordSession(SSL_SESSION* session) {
Ben Schwartz66810f62017-10-16 19:27:46 -0400345 std::lock_guard<std::mutex> guard(mLock);
Ben Schwartza13c23a2017-10-02 12:06:21 -0400346 mSessions.emplace_front(session);
347 if (mSessions.size() > 5) {
348 if (DBG) {
349 ALOGD("Too many sessions; trimming");
350 }
351 mSessions.pop_back();
352 }
353}
354
355void DnsTlsTransport::removeSession(SSL_SESSION* session) {
Ben Schwartz66810f62017-10-16 19:27:46 -0400356 std::lock_guard<std::mutex> guard(mLock);
Ben Schwartza13c23a2017-10-02 12:06:21 -0400357 if (session) {
358 // TODO: Consider implementing targeted removal.
359 mSessions.clear();
360 }
361}
362
363void DnsTlsTransport::sslDisconnect(bssl::UniquePtr<SSL> ssl, base::unique_fd fd) {
364 if (ssl) {
365 SSL_shutdown(ssl.get());
366 ssl.reset();
367 }
368 fd.reset();
Ben Schwartze7601812017-04-28 16:38:29 -0400369}
370
371bool DnsTlsTransport::sslWrite(int fd, SSL *ssl, const uint8_t *buffer, int len) {
372 if (DBG) {
373 ALOGD("%u Writing %d bytes", mMark, len);
374 }
375 for (;;) {
376 int ret = SSL_write(ssl, buffer, len);
377 if (ret == len) break; // SSL write complete;
378
379 if (ret < 1) {
380 const int ssl_err = SSL_get_error(ssl, ret);
381 switch (ssl_err) {
382 case SSL_ERROR_WANT_WRITE:
383 if (waitForWriting(fd) != 1) {
384 if (DBG) {
385 ALOGW("SSL_write error");
386 }
387 return false;
388 }
389 continue;
390 case 0:
391 break; // SSL write complete;
392 default:
393 if (DBG) {
394 ALOGW("SSL_write error %d", ssl_err);
395 }
396 return false;
397 }
398 }
399 }
400 if (DBG) {
401 ALOGD("%u Wrote %d bytes", mMark, len);
402 }
403 return true;
404}
405
406// Read exactly len bytes into buffer or fail
407bool DnsTlsTransport::sslRead(int fd, SSL *ssl, uint8_t *buffer, int len) {
408 int remaining = len;
409 while (remaining > 0) {
410 int ret = SSL_read(ssl, buffer + (len - remaining), remaining);
411 if (ret == 0) {
412 ALOGE("SSL socket closed with %i of %i bytes remaining", remaining, len);
413 return false;
414 }
415
416 if (ret < 0) {
417 const int ssl_err = SSL_get_error(ssl, ret);
418 if (ssl_err == SSL_ERROR_WANT_READ) {
419 if (waitForReading(fd) != 1) {
420 if (DBG) {
421 ALOGW("SSL_read error");
422 }
423 return false;
424 }
425 continue;
426 } else {
427 if (DBG) {
428 ALOGW("SSL_read error %d", ssl_err);
429 }
430 return false;
431 }
432 }
433
434 remaining -= ret;
435 }
436 return true;
437}
438
Ben Schwartz66810f62017-10-16 19:27:46 -0400439DnsTlsTransport::Response DnsTlsTransport::query(const uint8_t *query, size_t qlen,
Ben Schwartze7601812017-04-28 16:38:29 -0400440 uint8_t *response, size_t limit, int *resplen) {
Ben Schwartza13c23a2017-10-02 12:06:21 -0400441 android::base::unique_fd fd = makeConnectedSocket();
442 if (fd.get() < 0) {
443 ALOGD("%u makeConnectedSocket() failed with: %s", mMark, strerror(errno));
444 return Response::network_error;
Ben Schwartze7601812017-04-28 16:38:29 -0400445 }
Ben Schwartza13c23a2017-10-02 12:06:21 -0400446 bssl::UniquePtr<SSL> ssl = sslConnect(fd.get());
447 if (!ssl) {
Ben Schwartze7601812017-04-28 16:38:29 -0400448 return Response::network_error;
449 }
450
Ben Schwartza13c23a2017-10-02 12:06:21 -0400451 Response res = sendQuery(fd.get(), ssl.get(), query, qlen);
452 if (res == Response::success) {
453 res = readResponse(fd.get(), ssl.get(), query, response, limit, resplen);
454 }
455
456 sslDisconnect(std::move(ssl), std::move(fd));
457 return res;
458}
459
Ben Schwartz66810f62017-10-16 19:27:46 -0400460DnsTlsTransport::Response DnsTlsTransport::sendQuery(int fd, SSL* ssl,
461 const uint8_t *query, size_t qlen) {
Ben Schwartza13c23a2017-10-02 12:06:21 -0400462 if (DBG) {
463 ALOGD("sending query");
464 }
Ben Schwartze7601812017-04-28 16:38:29 -0400465 uint8_t queryHeader[2];
466 queryHeader[0] = qlen >> 8;
467 queryHeader[1] = qlen;
Ben Schwartza13c23a2017-10-02 12:06:21 -0400468 if (!sslWrite(fd, ssl, queryHeader, 2)) {
Ben Schwartze7601812017-04-28 16:38:29 -0400469 return Response::network_error;
470 }
Ben Schwartza13c23a2017-10-02 12:06:21 -0400471 if (!sslWrite(fd, ssl, query, qlen)) {
Ben Schwartze7601812017-04-28 16:38:29 -0400472 return Response::network_error;
473 }
474 if (DBG) {
475 ALOGD("%u SSL_write complete", mMark);
476 }
Ben Schwartza13c23a2017-10-02 12:06:21 -0400477 return Response::success;
478}
Ben Schwartze7601812017-04-28 16:38:29 -0400479
Ben Schwartz66810f62017-10-16 19:27:46 -0400480DnsTlsTransport::Response DnsTlsTransport::readResponse(int fd, SSL* ssl,
481 const uint8_t *query, uint8_t *response, size_t limit, int *resplen) {
Ben Schwartza13c23a2017-10-02 12:06:21 -0400482 if (DBG) {
483 ALOGD("reading response");
484 }
Ben Schwartze7601812017-04-28 16:38:29 -0400485 uint8_t responseHeader[2];
Ben Schwartza13c23a2017-10-02 12:06:21 -0400486 if (!sslRead(fd, ssl, responseHeader, 2)) {
Ben Schwartze7601812017-04-28 16:38:29 -0400487 if (DBG) {
488 ALOGW("%u Failed to read 2-byte length header", mMark);
489 }
490 return Response::network_error;
491 }
492 const uint16_t responseSize = (responseHeader[0] << 8) | responseHeader[1];
493 if (DBG) {
494 ALOGD("%u Expecting response of size %i", mMark, responseSize);
495 }
496 if (responseSize > limit) {
497 ALOGE("%u Response doesn't fit in output buffer: %i", mMark, responseSize);
498 return Response::limit_error;
499 }
Ben Schwartza13c23a2017-10-02 12:06:21 -0400500 if (!sslRead(fd, ssl, response, responseSize)) {
Ben Schwartze7601812017-04-28 16:38:29 -0400501 if (DBG) {
502 ALOGW("%u Failed to read %i bytes", mMark, responseSize);
503 }
504 return Response::network_error;
505 }
506 if (DBG) {
507 ALOGD("%u SSL_read complete", mMark);
508 }
509
510 if (response[0] != query[0] || response[1] != query[1]) {
511 ALOGE("reply query ID != query ID");
512 return Response::internal_error;
513 }
514
Ben Schwartze7601812017-04-28 16:38:29 -0400515 *resplen = responseSize;
516 return Response::success;
517}
518
Ben Schwartz52504622017-07-11 12:21:13 -0400519// static
Ben Schwartz66810f62017-10-16 19:27:46 -0400520bool DnsTlsTransport::validate(const DnsTlsServer& server, unsigned netid) {
Ben Schwartze7601812017-04-28 16:38:29 -0400521 if (DBG) {
522 ALOGD("Beginning validation on %u", netid);
523 }
524 // Generate "<random>-dnsotls-ds.metric.gstatic.com", which we will lookup through |ss| in
525 // order to prove that it is actually a working DNS over TLS server.
526 static const char kDnsSafeChars[] =
527 "abcdefhijklmnopqrstuvwxyz"
528 "ABCDEFHIJKLMNOPQRSTUVWXYZ"
529 "0123456789";
530 const auto c = [](uint8_t rnd) -> uint8_t {
531 return kDnsSafeChars[(rnd % ARRAY_SIZE(kDnsSafeChars))];
532 };
533 uint8_t rnd[8];
534 arc4random_buf(rnd, ARRAY_SIZE(rnd));
535 // We could try to use res_mkquery() here, but it's basically the same.
536 uint8_t query[] = {
537 rnd[6], rnd[7], // [0-1] query ID
538 1, 0, // [2-3] flags; query[2] = 1 for recursion desired (RD).
539 0, 1, // [4-5] QDCOUNT (number of queries)
540 0, 0, // [6-7] ANCOUNT (number of answers)
541 0, 0, // [8-9] NSCOUNT (number of name server records)
542 0, 0, // [10-11] ARCOUNT (number of additional records)
543 17, c(rnd[0]), c(rnd[1]), c(rnd[2]), c(rnd[3]), c(rnd[4]), c(rnd[5]),
544 '-', 'd', 'n', 's', 'o', 't', 'l', 's', '-', 'd', 's',
545 6, 'm', 'e', 't', 'r', 'i', 'c',
546 7, 'g', 's', 't', 'a', 't', 'i', 'c',
547 3, 'c', 'o', 'm',
548 0, // null terminator of FQDN (root TLD)
549 0, ns_t_aaaa, // QTYPE
550 0, ns_c_in // QCLASS
551 };
552 const int qlen = ARRAY_SIZE(query);
553
554 const int kRecvBufSize = 4 * 1024;
555 uint8_t recvbuf[kRecvBufSize];
556
557 // At validation time, we only know the netId, so we have to guess/compute the
558 // corresponding socket mark.
559 Fwmark fwmark;
560 fwmark.permission = PERMISSION_SYSTEM;
561 fwmark.explicitlySelected = true;
562 fwmark.protectedFromVpn = true;
563 fwmark.netId = netid;
564 unsigned mark = fwmark.intValue;
Ben Schwartze7601812017-04-28 16:38:29 -0400565 int replylen = 0;
Ben Schwartz66810f62017-10-16 19:27:46 -0400566 DnsTlsTransport transport(server, mark);
567 if (!transport.initialize()) {
568 return false;
569 }
570 transport.query(query, qlen, recvbuf, kRecvBufSize, &replylen);
Ben Schwartze7601812017-04-28 16:38:29 -0400571 if (replylen == 0) {
572 if (DBG) {
Ben Schwartz52504622017-07-11 12:21:13 -0400573 ALOGD("query failed");
Ben Schwartze7601812017-04-28 16:38:29 -0400574 }
575 return false;
576 }
577
578 if (replylen < NS_HFIXEDSZ) {
579 if (DBG) {
580 ALOGW("short response: %d", replylen);
581 }
582 return false;
583 }
584
585 const int qdcount = (recvbuf[4] << 8) | recvbuf[5];
586 if (qdcount != 1) {
587 ALOGW("reply query count != 1: %d", qdcount);
588 return false;
589 }
590
591 const int ancount = (recvbuf[6] << 8) | recvbuf[7];
592 if (DBG) {
593 ALOGD("%u answer count: %d", netid, ancount);
594 }
595
596 // TODO: Further validate the response contents (check for valid AAAA record, ...).
597 // Note that currently, integration tests rely on this function accepting a
598 // response with zero records.
599#if 0
600 for (int i = 0; i < resplen; i++) {
601 ALOGD("recvbuf[%d] = %d %c", i, recvbuf[i], recvbuf[i]);
602 }
603#endif
604 return true;
605}
606
607} // namespace net
608} // namespace android