blob: b4294e231304a59317d0a4599e786dc5fc96818f [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
Ben Schwartzded1b702017-10-25 14:41:02 -040017#define LOG_TAG "DnsTlsTransport"
Ben Schwartz33860762017-10-25 14:41:02 -040018//#define LOG_NDEBUG 0
Ben Schwartzded1b702017-10-25 14:41:02 -040019
Mike Yu5ae61542018-10-19 22:11:43 +080020#include "netd_resolv/DnsTlsTransport.h"
Ben Schwartze7601812017-04-28 16:38:29 -040021
22#include <arpa/inet.h>
23#include <arpa/nameser.h>
Ben Schwartze7601812017-04-28 16:38:29 -040024
Mike Yu5ae61542018-10-19 22:11:43 +080025#include "netd_resolv/DnsTlsSocketFactory.h"
26#include "netd_resolv/IDnsTlsSocketFactory.h"
Ben Schwartzded1b702017-10-25 14:41:02 -040027
Ben Schwartze7601812017-04-28 16:38:29 -040028#include "log/log.h"
29#include "Fwmark.h"
Ben Schwartze7601812017-04-28 16:38:29 -040030#include "Permission.h"
31
Ben Schwartze7601812017-04-28 16:38:29 -040032namespace android {
33namespace net {
34
Ben Schwartz33860762017-10-25 14:41:02 -040035std::future<DnsTlsTransport::Result> DnsTlsTransport::query(const netdutils::Slice query) {
Bernie Innocentiabf8a342018-08-10 15:17:16 +090036 std::lock_guard guard(mLock);
Ben Schwartz33860762017-10-25 14:41:02 -040037
38 auto record = mQueries.recordQuery(query);
39 if (!record) {
40 return std::async(std::launch::deferred, []{
41 return (Result) { .code = Response::internal_error };
42 });
Ben Schwartze7601812017-04-28 16:38:29 -040043 }
Ben Schwartzded1b702017-10-25 14:41:02 -040044
Ben Schwartz33860762017-10-25 14:41:02 -040045 if (!mSocket) {
46 ALOGV("No socket for query. Opening socket and sending.");
47 doConnect();
48 } else {
49 sendQuery(record->query);
Ben Schwartzded1b702017-10-25 14:41:02 -040050 }
51
Ben Schwartz33860762017-10-25 14:41:02 -040052 return std::move(record->result);
53}
54
55bool DnsTlsTransport::sendQuery(const DnsTlsQueryMap::Query q) {
56 // Strip off the ID number and send the new ID instead.
57 bool sent = mSocket->query(q.newId, netdutils::drop(q.query, 2));
58 if (sent) {
59 mQueries.markTried(q.newId);
60 }
61 return sent;
62}
63
64void DnsTlsTransport::doConnect() {
65 ALOGV("Constructing new socket");
66 mSocket = mFactory->createDnsTlsSocket(mServer, mMark, this, &mCache);
67
68 if (mSocket) {
69 auto queries = mQueries.getAll();
70 ALOGV("Initialization succeeded. Reissuing %zu queries.", queries.size());
71 for(auto& q : queries) {
72 if (!sendQuery(q)) {
73 break;
74 }
75 }
76 } else {
77 ALOGV("Initialization failed.");
78 mSocket.reset();
79 ALOGV("Failing all pending queries.");
80 mQueries.clear();
81 }
82}
83
84void DnsTlsTransport::onResponse(std::vector<uint8_t> response) {
85 mQueries.onResponse(std::move(response));
86}
87
88void DnsTlsTransport::onClosed() {
Bernie Innocentiabf8a342018-08-10 15:17:16 +090089 std::lock_guard guard(mLock);
Ben Schwartz33860762017-10-25 14:41:02 -040090 if (mClosing) {
91 return;
92 }
93 // Move remaining operations to a new thread.
94 // This is necessary because
95 // 1. onClosed is currently running on a thread that blocks mSocket's destructor
96 // 2. doReconnect will call that destructor
97 if (mReconnectThread) {
98 // Complete cleanup of a previous reconnect thread, if present.
99 mReconnectThread->join();
100 // Joining a thread that is trying to acquire mLock, while holding mLock,
101 // looks like it risks a deadlock. However, a deadlock will not occur because
102 // once onClosed is called, it cannot be called again until after doReconnect
103 // acquires mLock.
104 }
105 mReconnectThread.reset(new std::thread(&DnsTlsTransport::doReconnect, this));
106}
107
108void DnsTlsTransport::doReconnect() {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900109 std::lock_guard guard(mLock);
Ben Schwartz33860762017-10-25 14:41:02 -0400110 if (mClosing) {
111 return;
112 }
113 mQueries.cleanup();
114 if (!mQueries.empty()) {
115 ALOGV("Fast reconnect to retry remaining queries");
116 doConnect();
117 } else {
118 ALOGV("No pending queries. Going idle.");
119 mSocket.reset();
120 }
Ben Schwartze7601812017-04-28 16:38:29 -0400121}
122
Ben Schwartzded1b702017-10-25 14:41:02 -0400123DnsTlsTransport::~DnsTlsTransport() {
Ben Schwartz33860762017-10-25 14:41:02 -0400124 ALOGV("Destructor");
125 {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900126 std::lock_guard guard(mLock);
Ben Schwartz33860762017-10-25 14:41:02 -0400127 ALOGV("Locked destruction procedure");
128 mQueries.clear();
129 mClosing = true;
130 }
131 // It's possible that a reconnect thread was spawned and waiting for mLock.
132 // It's safe for that thread to run now because mClosing is true (and mQueries is empty),
133 // but we need to wait for it to finish before allowing destruction to proceed.
134 if (mReconnectThread) {
135 ALOGV("Waiting for reconnect thread to terminate");
136 mReconnectThread->join();
137 mReconnectThread.reset();
138 }
139 // Ensure that the socket is destroyed, and can clean up its callback threads,
140 // before any of this object's fields become invalid.
141 mSocket.reset();
142 ALOGV("Destructor completed");
Ben Schwartza13c23a2017-10-02 12:06:21 -0400143}
144
145// static
Ben Schwartzded1b702017-10-25 14:41:02 -0400146// TODO: Use this function to preheat the session cache.
147// That may require moving it to DnsTlsDispatcher.
Ben Schwartz66810f62017-10-16 19:27:46 -0400148bool DnsTlsTransport::validate(const DnsTlsServer& server, unsigned netid) {
Ben Schwartzded1b702017-10-25 14:41:02 -0400149 ALOGV("Beginning validation on %u", netid);
Ben Schwartze7601812017-04-28 16:38:29 -0400150 // Generate "<random>-dnsotls-ds.metric.gstatic.com", which we will lookup through |ss| in
151 // order to prove that it is actually a working DNS over TLS server.
152 static const char kDnsSafeChars[] =
153 "abcdefhijklmnopqrstuvwxyz"
154 "ABCDEFHIJKLMNOPQRSTUVWXYZ"
155 "0123456789";
156 const auto c = [](uint8_t rnd) -> uint8_t {
Mike Yu5ae61542018-10-19 22:11:43 +0800157 return kDnsSafeChars[(rnd % std::size(kDnsSafeChars))];
Ben Schwartze7601812017-04-28 16:38:29 -0400158 };
159 uint8_t rnd[8];
Mike Yu5ae61542018-10-19 22:11:43 +0800160 arc4random_buf(rnd, std::size(rnd));
Ben Schwartze7601812017-04-28 16:38:29 -0400161 // We could try to use res_mkquery() here, but it's basically the same.
162 uint8_t query[] = {
163 rnd[6], rnd[7], // [0-1] query ID
164 1, 0, // [2-3] flags; query[2] = 1 for recursion desired (RD).
165 0, 1, // [4-5] QDCOUNT (number of queries)
166 0, 0, // [6-7] ANCOUNT (number of answers)
167 0, 0, // [8-9] NSCOUNT (number of name server records)
168 0, 0, // [10-11] ARCOUNT (number of additional records)
169 17, c(rnd[0]), c(rnd[1]), c(rnd[2]), c(rnd[3]), c(rnd[4]), c(rnd[5]),
170 '-', 'd', 'n', 's', 'o', 't', 'l', 's', '-', 'd', 's',
171 6, 'm', 'e', 't', 'r', 'i', 'c',
172 7, 'g', 's', 't', 'a', 't', 'i', 'c',
173 3, 'c', 'o', 'm',
174 0, // null terminator of FQDN (root TLD)
175 0, ns_t_aaaa, // QTYPE
176 0, ns_c_in // QCLASS
177 };
Mike Yu5ae61542018-10-19 22:11:43 +0800178 const int qlen = std::size(query);
Ben Schwartze7601812017-04-28 16:38:29 -0400179
Ben Schwartze7601812017-04-28 16:38:29 -0400180 // At validation time, we only know the netId, so we have to guess/compute the
181 // corresponding socket mark.
182 Fwmark fwmark;
183 fwmark.permission = PERMISSION_SYSTEM;
184 fwmark.explicitlySelected = true;
185 fwmark.protectedFromVpn = true;
186 fwmark.netId = netid;
187 unsigned mark = fwmark.intValue;
Ben Schwartze7601812017-04-28 16:38:29 -0400188 int replylen = 0;
Ben Schwartzded1b702017-10-25 14:41:02 -0400189 DnsTlsSocketFactory factory;
190 DnsTlsTransport transport(server, mark, &factory);
Ben Schwartz33860762017-10-25 14:41:02 -0400191 auto r = transport.query(Slice(query, qlen)).get();
Ben Schwartzded1b702017-10-25 14:41:02 -0400192 if (r.code != Response::success) {
193 ALOGV("query failed");
Ben Schwartze7601812017-04-28 16:38:29 -0400194 return false;
195 }
196
Ben Schwartzded1b702017-10-25 14:41:02 -0400197 const std::vector<uint8_t>& recvbuf = r.response;
198 if (recvbuf.size() < NS_HFIXEDSZ) {
199 ALOGW("short response: %d", replylen);
Ben Schwartze7601812017-04-28 16:38:29 -0400200 return false;
201 }
202
203 const int qdcount = (recvbuf[4] << 8) | recvbuf[5];
204 if (qdcount != 1) {
205 ALOGW("reply query count != 1: %d", qdcount);
206 return false;
207 }
208
209 const int ancount = (recvbuf[6] << 8) | recvbuf[7];
Ben Schwartzded1b702017-10-25 14:41:02 -0400210 ALOGV("%u answer count: %d", netid, ancount);
Ben Schwartze7601812017-04-28 16:38:29 -0400211
212 // TODO: Further validate the response contents (check for valid AAAA record, ...).
213 // Note that currently, integration tests rely on this function accepting a
214 // response with zero records.
215#if 0
216 for (int i = 0; i < resplen; i++) {
217 ALOGD("recvbuf[%d] = %d %c", i, recvbuf[i], recvbuf[i]);
218 }
219#endif
220 return true;
221}
222
Ben Schwartzded1b702017-10-25 14:41:02 -0400223} // end of namespace net
224} // end of namespace android