1 /**
2 * Copyright (c) 2019, 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_metrics_listener.h"
18
19 #include <thread>
20
21 #include <android-base/chrono_utils.h>
22 #include <android-base/format.h>
23
24 namespace android {
25 namespace net {
26 namespace metrics {
27
28 using android::base::ScopedLockAssertion;
29 using std::chrono::milliseconds;
30
31 constexpr milliseconds kRetryIntervalMs{20};
32 constexpr milliseconds kEventTimeoutMs{5000};
33
operator ==(const DnsMetricsListener::DnsEvent & o) const34 bool DnsMetricsListener::DnsEvent::operator==(const DnsMetricsListener::DnsEvent& o) const {
35 return std::tie(netId, eventType, returnCode, hostname, ipAddresses, ipAddressesCount) ==
36 std::tie(o.netId, o.eventType, o.returnCode, o.hostname, o.ipAddresses,
37 o.ipAddressesCount);
38 }
39
operator <<(std::ostream & os,const DnsMetricsListener::DnsEvent & data)40 std::ostream& operator<<(std::ostream& os, const DnsMetricsListener::DnsEvent& data) {
41 return os << fmt::format("[{}, {}, {}, {}, [{}], {}]", data.netId, data.eventType,
42 data.returnCode, data.hostname, fmt::join(data.ipAddresses, ", "),
43 data.ipAddressesCount);
44 }
45
onNat64PrefixEvent(int32_t netId,bool added,const std::string & prefixString,int32_t)46 ::ndk::ScopedAStatus DnsMetricsListener::onNat64PrefixEvent(int32_t netId, bool added,
47 const std::string& prefixString,
48 int32_t /*prefixLength*/) {
49 std::lock_guard lock(mMutex);
50 mUnexpectedNat64PrefixUpdates++;
51 if (netId == mNetId) mNat64Prefix = added ? prefixString : "";
52 return ::ndk::ScopedAStatus::ok();
53 }
54
onPrivateDnsValidationEvent(int32_t netId,const std::string & ipAddress,const std::string &,bool validated)55 ::ndk::ScopedAStatus DnsMetricsListener::onPrivateDnsValidationEvent(
56 int32_t netId, const std::string& ipAddress, const std::string& /*hostname*/,
57 bool validated) {
58 {
59 std::lock_guard lock(mMutex);
60 // keep updating the server to have latest validation status.
61 mValidationRecords.insert_or_assign({netId, ipAddress}, validated);
62 }
63 mCv.notify_one();
64 return ::ndk::ScopedAStatus::ok();
65 }
66
onDnsEvent(int32_t netId,int32_t eventType,int32_t returnCode,int32_t,const std::string & hostname,const std::vector<std::string> & ipAddresses,int32_t ipAddressesCount,int32_t)67 ::ndk::ScopedAStatus DnsMetricsListener::onDnsEvent(int32_t netId, int32_t eventType,
68 int32_t returnCode, int32_t /*latencyMs*/,
69 const std::string& hostname,
70 const std::vector<std::string>& ipAddresses,
71 int32_t ipAddressesCount, int32_t /*uid*/) {
72 std::lock_guard lock(mMutex);
73 if (netId == mNetId) {
74 mDnsEventRecords.push(
75 {netId, eventType, returnCode, hostname, ipAddresses, ipAddressesCount});
76 }
77 return ::ndk::ScopedAStatus::ok();
78 }
79
waitForNat64Prefix(ExpectNat64PrefixStatus status,milliseconds timeout)80 bool DnsMetricsListener::waitForNat64Prefix(ExpectNat64PrefixStatus status, milliseconds timeout) {
81 android::base::Timer t;
82 while (t.duration() < timeout) {
83 {
84 std::lock_guard lock(mMutex);
85 if ((status == EXPECT_FOUND && !mNat64Prefix.empty()) ||
86 (status == EXPECT_NOT_FOUND && mNat64Prefix.empty())) {
87 mUnexpectedNat64PrefixUpdates--;
88 return true;
89 }
90 }
91 std::this_thread::sleep_for(kRetryIntervalMs);
92 }
93 return false;
94 }
95
waitForPrivateDnsValidation(const std::string & serverAddr,const bool validated)96 bool DnsMetricsListener::waitForPrivateDnsValidation(const std::string& serverAddr,
97 const bool validated) {
98 const auto now = std::chrono::steady_clock::now();
99
100 std::unique_lock lock(mMutex);
101 ScopedLockAssertion assume_lock(mMutex);
102
103 // onPrivateDnsValidationEvent() might already be invoked. Search for the record first.
104 do {
105 if (findAndRemoveValidationRecord({mNetId, serverAddr}, validated)) return true;
106 } while (mCv.wait_until(lock, now + kEventTimeoutMs) != std::cv_status::timeout);
107
108 // Timeout.
109 return false;
110 }
111
findAndRemoveValidationRecord(const ServerKey & key,const bool value)112 bool DnsMetricsListener::findAndRemoveValidationRecord(const ServerKey& key, const bool value) {
113 auto it = mValidationRecords.find(key);
114 if (it != mValidationRecords.end() && it->second == value) {
115 mValidationRecords.erase(it);
116 return true;
117 }
118 return false;
119 }
120
popDnsEvent()121 std::optional<DnsMetricsListener::DnsEvent> DnsMetricsListener::popDnsEvent() {
122 // Wait until the queue is not empty or timeout.
123 android::base::Timer t;
124 while (t.duration() < milliseconds{1000}) {
125 {
126 std::lock_guard lock(mMutex);
127 if (!mDnsEventRecords.empty()) break;
128 }
129 std::this_thread::sleep_for(kRetryIntervalMs);
130 }
131
132 std::lock_guard lock(mMutex);
133 if (mDnsEventRecords.empty()) return std::nullopt;
134
135 auto ret = mDnsEventRecords.front();
136 mDnsEventRecords.pop();
137 return ret;
138 }
139
140 } // namespace metrics
141 } // namespace net
142 } // namespace android
143