blob: 3453765238ae3dede2b5a1d9a45cd59fcb5a7ed5 [file] [log] [blame]
Mike Yubab3daa2018-10-19 22:11:43 +08001/*
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
Ken Chen5471dca2019-04-15 15:25:35 +080017#define LOG_TAG "resolv"
Mike Yubab3daa2018-10-19 22:11:43 +080018
Bernie Innocentiec4219b2019-01-30 11:16:36 +090019#include "DnsTlsTransport.h"
Mike Yubab3daa2018-10-19 22:11:43 +080020
Mike Yu04f1d482019-08-08 11:09:32 +080021#include <Fwmark.h>
22#include <android-base/logging.h>
23#include <android-base/stringprintf.h>
Mike Yubab3daa2018-10-19 22:11:43 +080024#include <arpa/inet.h>
25#include <arpa/nameser.h>
Mike Yu04f1d482019-08-08 11:09:32 +080026#include <netdutils/ThreadUtil.h>
Mike Yubab3daa2018-10-19 22:11:43 +080027
Bernie Innocentiec4219b2019-01-30 11:16:36 +090028#include "DnsTlsSocketFactory.h"
29#include "IDnsTlsSocketFactory.h"
Mike Yubab3daa2018-10-19 22:11:43 +080030
Mike Yubab3daa2018-10-19 22:11:43 +080031namespace android {
32namespace net {
33
34std::future<DnsTlsTransport::Result> DnsTlsTransport::query(const netdutils::Slice query) {
35 std::lock_guard guard(mLock);
36
37 auto record = mQueries.recordQuery(query);
38 if (!record) {
39 return std::async(std::launch::deferred, []{
40 return (Result) { .code = Response::internal_error };
41 });
42 }
43
44 if (!mSocket) {
chenbruceaff85842019-05-31 15:46:42 +080045 LOG(DEBUG) << "No socket for query. Opening socket and sending.";
Mike Yubab3daa2018-10-19 22:11:43 +080046 doConnect();
47 } else {
48 sendQuery(record->query);
49 }
50
51 return std::move(record->result);
52}
53
54bool DnsTlsTransport::sendQuery(const DnsTlsQueryMap::Query q) {
55 // Strip off the ID number and send the new ID instead.
56 bool sent = mSocket->query(q.newId, netdutils::drop(q.query, 2));
57 if (sent) {
58 mQueries.markTried(q.newId);
59 }
60 return sent;
61}
62
63void DnsTlsTransport::doConnect() {
chenbruceaff85842019-05-31 15:46:42 +080064 LOG(DEBUG) << "Constructing new socket";
Mike Yubab3daa2018-10-19 22:11:43 +080065 mSocket = mFactory->createDnsTlsSocket(mServer, mMark, this, &mCache);
66
67 if (mSocket) {
68 auto queries = mQueries.getAll();
chenbruceaff85842019-05-31 15:46:42 +080069 LOG(DEBUG) << "Initialization succeeded. Reissuing " << queries.size() << " queries.";
Mike Yubab3daa2018-10-19 22:11:43 +080070 for(auto& q : queries) {
71 if (!sendQuery(q)) {
72 break;
73 }
74 }
75 } else {
chenbruceaff85842019-05-31 15:46:42 +080076 LOG(DEBUG) << "Initialization failed.";
Mike Yubab3daa2018-10-19 22:11:43 +080077 mSocket.reset();
chenbruceaff85842019-05-31 15:46:42 +080078 LOG(DEBUG) << "Failing all pending queries.";
Mike Yubab3daa2018-10-19 22:11:43 +080079 mQueries.clear();
80 }
81}
82
83void DnsTlsTransport::onResponse(std::vector<uint8_t> response) {
84 mQueries.onResponse(std::move(response));
85}
86
87void DnsTlsTransport::onClosed() {
88 std::lock_guard guard(mLock);
89 if (mClosing) {
90 return;
91 }
92 // Move remaining operations to a new thread.
93 // This is necessary because
94 // 1. onClosed is currently running on a thread that blocks mSocket's destructor
95 // 2. doReconnect will call that destructor
96 if (mReconnectThread) {
97 // Complete cleanup of a previous reconnect thread, if present.
98 mReconnectThread->join();
99 // Joining a thread that is trying to acquire mLock, while holding mLock,
100 // looks like it risks a deadlock. However, a deadlock will not occur because
101 // once onClosed is called, it cannot be called again until after doReconnect
102 // acquires mLock.
103 }
104 mReconnectThread.reset(new std::thread(&DnsTlsTransport::doReconnect, this));
105}
106
107void DnsTlsTransport::doReconnect() {
108 std::lock_guard guard(mLock);
Mike Yu04f1d482019-08-08 11:09:32 +0800109 Fwmark mark;
110 mark.intValue = mMark;
111 netdutils::setThreadName(android::base::StringPrintf("TlsReconn_%u", mark.netId).c_str());
Mike Yubab3daa2018-10-19 22:11:43 +0800112 if (mClosing) {
113 return;
114 }
115 mQueries.cleanup();
116 if (!mQueries.empty()) {
chenbruceaff85842019-05-31 15:46:42 +0800117 LOG(DEBUG) << "Fast reconnect to retry remaining queries";
Mike Yubab3daa2018-10-19 22:11:43 +0800118 doConnect();
119 } else {
chenbruceaff85842019-05-31 15:46:42 +0800120 LOG(DEBUG) << "No pending queries. Going idle.";
Mike Yubab3daa2018-10-19 22:11:43 +0800121 mSocket.reset();
122 }
123}
124
125DnsTlsTransport::~DnsTlsTransport() {
chenbruceaff85842019-05-31 15:46:42 +0800126 LOG(DEBUG) << "Destructor";
Mike Yubab3daa2018-10-19 22:11:43 +0800127 {
128 std::lock_guard guard(mLock);
chenbruceaff85842019-05-31 15:46:42 +0800129 LOG(DEBUG) << "Locked destruction procedure";
Mike Yubab3daa2018-10-19 22:11:43 +0800130 mQueries.clear();
131 mClosing = true;
132 }
133 // It's possible that a reconnect thread was spawned and waiting for mLock.
134 // It's safe for that thread to run now because mClosing is true (and mQueries is empty),
135 // but we need to wait for it to finish before allowing destruction to proceed.
136 if (mReconnectThread) {
chenbruceaff85842019-05-31 15:46:42 +0800137 LOG(DEBUG) << "Waiting for reconnect thread to terminate";
Mike Yubab3daa2018-10-19 22:11:43 +0800138 mReconnectThread->join();
139 mReconnectThread.reset();
140 }
141 // Ensure that the socket is destroyed, and can clean up its callback threads,
142 // before any of this object's fields become invalid.
143 mSocket.reset();
chenbruceaff85842019-05-31 15:46:42 +0800144 LOG(DEBUG) << "Destructor completed";
Mike Yubab3daa2018-10-19 22:11:43 +0800145}
146
147// static
148// TODO: Use this function to preheat the session cache.
149// That may require moving it to DnsTlsDispatcher.
Mike Yuf48c3c72018-11-02 13:30:04 +0800150bool DnsTlsTransport::validate(const DnsTlsServer& server, unsigned netid, uint32_t mark) {
chenbruceaff85842019-05-31 15:46:42 +0800151 LOG(DEBUG) << "Beginning validation on " << netid;
Mike Yubab3daa2018-10-19 22:11:43 +0800152 // Generate "<random>-dnsotls-ds.metric.gstatic.com", which we will lookup through |ss| in
153 // order to prove that it is actually a working DNS over TLS server.
154 static const char kDnsSafeChars[] =
155 "abcdefhijklmnopqrstuvwxyz"
156 "ABCDEFHIJKLMNOPQRSTUVWXYZ"
157 "0123456789";
158 const auto c = [](uint8_t rnd) -> uint8_t {
159 return kDnsSafeChars[(rnd % std::size(kDnsSafeChars))];
160 };
161 uint8_t rnd[8];
162 arc4random_buf(rnd, std::size(rnd));
163 // We could try to use res_mkquery() here, but it's basically the same.
164 uint8_t query[] = {
165 rnd[6], rnd[7], // [0-1] query ID
166 1, 0, // [2-3] flags; query[2] = 1 for recursion desired (RD).
167 0, 1, // [4-5] QDCOUNT (number of queries)
168 0, 0, // [6-7] ANCOUNT (number of answers)
169 0, 0, // [8-9] NSCOUNT (number of name server records)
170 0, 0, // [10-11] ARCOUNT (number of additional records)
171 17, c(rnd[0]), c(rnd[1]), c(rnd[2]), c(rnd[3]), c(rnd[4]), c(rnd[5]),
172 '-', 'd', 'n', 's', 'o', 't', 'l', 's', '-', 'd', 's',
173 6, 'm', 'e', 't', 'r', 'i', 'c',
174 7, 'g', 's', 't', 'a', 't', 'i', 'c',
175 3, 'c', 'o', 'm',
176 0, // null terminator of FQDN (root TLD)
177 0, ns_t_aaaa, // QTYPE
178 0, ns_c_in // QCLASS
179 };
180 const int qlen = std::size(query);
181
Mike Yubab3daa2018-10-19 22:11:43 +0800182 int replylen = 0;
183 DnsTlsSocketFactory factory;
184 DnsTlsTransport transport(server, mark, &factory);
Bernie Innocentiec4219b2019-01-30 11:16:36 +0900185 auto r = transport.query(netdutils::Slice(query, qlen)).get();
Mike Yubab3daa2018-10-19 22:11:43 +0800186 if (r.code != Response::success) {
chenbruceaff85842019-05-31 15:46:42 +0800187 LOG(DEBUG) << "query failed";
Mike Yubab3daa2018-10-19 22:11:43 +0800188 return false;
189 }
190
191 const std::vector<uint8_t>& recvbuf = r.response;
192 if (recvbuf.size() < NS_HFIXEDSZ) {
chenbruceaff85842019-05-31 15:46:42 +0800193 LOG(WARNING) << "short response: " << replylen;
Mike Yubab3daa2018-10-19 22:11:43 +0800194 return false;
195 }
196
197 const int qdcount = (recvbuf[4] << 8) | recvbuf[5];
198 if (qdcount != 1) {
chenbruceaff85842019-05-31 15:46:42 +0800199 LOG(WARNING) << "reply query count != 1: " << qdcount;
Mike Yubab3daa2018-10-19 22:11:43 +0800200 return false;
201 }
202
203 const int ancount = (recvbuf[6] << 8) | recvbuf[7];
chenbruceaff85842019-05-31 15:46:42 +0800204 LOG(DEBUG) << netid << " answer count: " << ancount;
Mike Yubab3daa2018-10-19 22:11:43 +0800205
206 // TODO: Further validate the response contents (check for valid AAAA record, ...).
207 // Note that currently, integration tests rely on this function accepting a
208 // response with zero records.
chenbruceaff85842019-05-31 15:46:42 +0800209
Mike Yubab3daa2018-10-19 22:11:43 +0800210 return true;
211}
212
213} // end of namespace net
214} // end of namespace android