1 /*
2  * Copyright (C) 2021 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 "doh_frontend.h"
18 
19 #define LOG_TAG "DohFrontend"
20 
21 #include <thread>
22 
23 #include <android-base/chrono_utils.h>
24 #include <android-base/file.h>
25 #include <android-base/logging.h>
26 #include <gtest/gtest.h>
27 
28 #include "dns_tls_certificate.h"
29 
30 using std::chrono::milliseconds;
31 
32 namespace test {
33 
34 constexpr milliseconds kEventTimeoutMs{5000};
35 constexpr milliseconds kRetryIntervalMs{20};
36 
~DohFrontend()37 DohFrontend::~DohFrontend() {
38     if (mRustDoh) {
39         stopServer();
40         rust::frontend_delete(mRustDoh);
41     }
42 }
43 
startServer()44 bool DohFrontend::startServer() {
45     std::lock_guard guard(mMutex);
46     if (mRustDoh == nullptr) {
47         mRustDoh = rust::frontend_new(mAddress.c_str(), mService.c_str(), mBackendAddress.c_str(),
48                                       mBackendService.c_str());
49         if (mRustDoh == nullptr) {
50             LOG(ERROR) << "Failed to create rust DoH frontend";
51             return false;
52         }
53     }
54 
55     rust::frontend_set_certificate(mRustDoh, kCertificate);
56     rust::frontend_set_private_key(mRustDoh, kPrivatekey);
57 
58     return rust::frontend_start(mRustDoh);
59 }
60 
stopServer()61 bool DohFrontend::stopServer() {
62     std::lock_guard guard(mMutex);
63     if (!mRustDoh) return false;
64 
65     return rust::frontend_stop(mRustDoh);
66 }
67 
queries() const68 int DohFrontend::queries() const {
69     std::lock_guard guard(mMutex);
70     if (!mRustDoh) return 0;
71 
72     rust::Stats stats;
73     rust::frontend_stats(mRustDoh, &stats);
74     return stats.queries_received;
75 }
76 
connections() const77 int DohFrontend::connections() const {
78     std::lock_guard guard(mMutex);
79     if (!mRustDoh) return 0;
80 
81     rust::Stats stats;
82     rust::frontend_stats(mRustDoh, &stats);
83     return stats.connections_accepted;
84 }
85 
aliveConnections() const86 int DohFrontend::aliveConnections() const {
87     std::lock_guard guard(mMutex);
88     if (!mRustDoh) return 0;
89 
90     rust::Stats stats;
91     rust::frontend_stats(mRustDoh, &stats);
92     return stats.alive_connections;
93 }
94 
resumedConnections() const95 int DohFrontend::resumedConnections() const {
96     std::lock_guard guard(mMutex);
97     if (!mRustDoh) return 0;
98 
99     rust::Stats stats;
100     rust::frontend_stats(mRustDoh, &stats);
101     return stats.resumed_connections;
102 }
103 
earlyDataConnections() const104 int DohFrontend::earlyDataConnections() const {
105     std::lock_guard guard(mMutex);
106     if (!mRustDoh) return 0;
107 
108     rust::Stats stats;
109     rust::frontend_stats(mRustDoh, &stats);
110     return stats.early_data_connections;
111 }
112 
clearQueries()113 void DohFrontend::clearQueries() {
114     std::lock_guard guard(mMutex);
115     if (mRustDoh) {
116         frontend_stats_clear_queries(mRustDoh);
117 
118         // Because frontend_stats_clear_queries() is asynchronous, query the stat here to ensure
119         // that mRustDoh reset the query count before clearQueries() returns.
120         rust::Stats stats;
121         rust::frontend_stats(mRustDoh, &stats);
122         if (stats.queries_received != 0) {
123             LOG(ERROR) << "queries_received is not 0";
124         }
125     }
126 }
127 
setMaxIdleTimeout(uint64_t value)128 bool DohFrontend::setMaxIdleTimeout(uint64_t value) {
129     std::lock_guard guard(mMutex);
130     if (!mRustDoh) return false;
131 
132     return frontend_set_max_idle_timeout(mRustDoh, value);
133 }
134 
setMaxBufferSize(uint64_t value)135 bool DohFrontend::setMaxBufferSize(uint64_t value) {
136     std::lock_guard guard(mMutex);
137     if (!mRustDoh) return false;
138 
139     return frontend_set_max_buffer_size(mRustDoh, value);
140 }
141 
setMaxStreamsBidi(uint64_t value)142 bool DohFrontend::setMaxStreamsBidi(uint64_t value) {
143     std::lock_guard guard(mMutex);
144     if (!mRustDoh) return false;
145 
146     return frontend_set_max_streams_bidi(mRustDoh, value);
147 }
148 
block_sending(bool block)149 bool DohFrontend::block_sending(bool block) {
150     std::lock_guard guard(mMutex);
151     if (!mRustDoh) return false;
152 
153     return frontend_block_sending(mRustDoh, block);
154 }
155 
setResetStreamId(uint64_t value)156 bool DohFrontend::setResetStreamId(uint64_t value) {
157     std::lock_guard guard(mMutex);
158     if (!mRustDoh) return false;
159 
160     return frontend_set_reset_stream_id(mRustDoh, value);
161 }
162 
waitForAllClientsDisconnected() const163 bool DohFrontend::waitForAllClientsDisconnected() const {
164     android::base::Timer t;
165     while (t.duration() < kEventTimeoutMs) {
166         if (aliveConnections() == 0) return true;
167         std::this_thread::sleep_for(kRetryIntervalMs);
168     }
169     return false;
170 }
171 
172 }  // namespace test
173