1 /*
2 * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <fstream>
17 #include <sstream>
18
19 #include "dns_quality_diag.h"
20 #include "net_handle.h"
21 #include "net_conn_client.h"
22
23 namespace OHOS::nmd {
24 namespace {
25 using namespace OHOS::NetsysNative;
26 }
27
28 const char *DNS_DIAG_WORK_THREAD = "DNS_DIAG_WORK_THREAD";
29 const char *HW_HICLOUD_ADDR = "connectivitycheck.platform.hicloud.com";
30 const uint32_t MAX_RESULT_SIZE = 32;
31 const char *URL_CFG_FILE = "/system/etc/netdetectionurl.conf";
32 const char *DNS_URL_HEADER = "DnsProbeUrl:";
33 const char NEW_LINE_STR = '\n';
34 const uint32_t TIME_DELAY = 500;
35
DnsQualityDiag()36 DnsQualityDiag::DnsQualityDiag()
37 : defaultNetId_(0),
38 monitor_loop_delay(TIME_DELAY),
39 report_delay(TIME_DELAY),
40 handler_started(false),
41 handler_(nullptr)
42 {
43 InitHandler();
44 load_query_addr(HW_HICLOUD_ADDR);
45 }
46
GetInstance()47 DnsQualityDiag &DnsQualityDiag::GetInstance()
48 {
49 static DnsQualityDiag instance;
50 return instance;
51 }
52
InitHandler()53 int32_t DnsQualityDiag::InitHandler()
54 {
55 if (handler_ == nullptr) {
56 std::shared_ptr<AppExecFwk::EventRunner> runner_ =
57 AppExecFwk::EventRunner::Create(DNS_DIAG_WORK_THREAD);
58 if (!runner_) {
59 NETNATIVE_LOGE("Create net policy work event runner.");
60 } else {
61 handler_ = std::make_shared<DnsQualityEventHandler>(runner_);
62 }
63 }
64 return 0;
65 }
66
SendHealthReport(NetsysNative::NetDnsHealthReport healthreport)67 int32_t DnsQualityDiag::SendHealthReport(NetsysNative::NetDnsHealthReport healthreport)
68 {
69 for (auto cb: healthListeners_) {
70 cb->OnDnsHealthReport(healthreport);
71 }
72
73 return 0;
74 }
ParseReportAddr(uint32_t size,AddrInfo * addrinfo,NetsysNative::NetDnsResultReport & report)75 int32_t DnsQualityDiag::ParseReportAddr(uint32_t size, AddrInfo* addrinfo, NetsysNative::NetDnsResultReport &report)
76 {
77 for (uint8_t i = 0; i < size; i++) {
78 NetsysNative::NetDnsResultAddrInfo ai;
79 AddrInfo *tmp = &(addrinfo[i]);
80 void* addr = NULL;
81 char c_addr[INET6_ADDRSTRLEN];
82 switch (tmp->aiFamily) {
83 case AF_INET:
84 ai.type_ = NetsysNative::ADDR_TYPE_IPV4;
85 addr = &(tmp->aiAddr.sin.sin_addr);
86 break;
87 case AF_INET6:
88 ai.type_ = NetsysNative::ADDR_TYPE_IPV6;
89 addr = &(tmp->aiAddr.sin6.sin6_addr);
90 break;
91 }
92 inet_ntop(tmp->aiFamily, addr, c_addr, sizeof(c_addr));
93 ai.addr_ = c_addr;
94 if (report.addrlist_.size() < MAX_RESULT_SIZE) {
95 report.addrlist_.push_back(ai);
96 } else {
97 break;
98 }
99 }
100 return 0;
101 }
102
ReportDnsResult(uint16_t netId,uint16_t uid,uint32_t pid,int32_t usedtime,char * name,uint32_t size,int32_t failreason,QueryParam queryParam,AddrInfo * addrinfo)103 int32_t DnsQualityDiag::ReportDnsResult(uint16_t netId, uint16_t uid, uint32_t pid, int32_t usedtime,
104 char* name, uint32_t size, int32_t failreason, QueryParam queryParam, AddrInfo* addrinfo)
105 {
106 bool reportSizeReachLimit = (report_.size() >= MAX_RESULT_SIZE);
107
108 NETNATIVE_LOG_D("ReportDnsResult: %{public}d, %{public}d, %{public}d, %{public}d, %{public}d, %{public}d",
109 netId, uid, pid, usedtime, size, failreason);
110
111 if (queryParam.type == 1) {
112 NETNATIVE_LOG_D("ReportDnsResult: query from Netmanager ignore report");
113 return 0;
114 }
115
116 if (!reportSizeReachLimit) {
117 NetsysNative::NetDnsResultReport report;
118 report.netid_ = netId;
119 report.uid_ = uid;
120 report.pid_ = pid;
121 report.timeused_ = static_cast<uint32_t>(usedtime);
122 report.queryresult_ = static_cast<uint32_t>(failreason);
123 report.host_ = name;
124 if (failreason == 0) {
125 ParseReportAddr(size, addrinfo, report);
126 }
127 NETNATIVE_LOG_D("ReportDnsResult: %{public}s", report.host_.c_str());
128 std::shared_ptr<NetsysNative::NetDnsResultReport> rpt =
129 std::make_shared<NetsysNative::NetDnsResultReport>(report);
130 auto event = AppExecFwk::InnerEvent::Get(DnsQualityEventHandler::MSG_DNS_NEW_REPORT, rpt);
131 handler_->SendEvent(event);
132 }
133
134 return 0;
135 }
136
RegisterResultListener(const sptr<INetDnsResultCallback> & callback,uint32_t timeStep)137 int32_t DnsQualityDiag::RegisterResultListener(const sptr<INetDnsResultCallback> &callback, uint32_t timeStep)
138 {
139 report_delay = std::max(report_delay, timeStep);
140
141 std::unique_lock<std::mutex> locker(resultListenersMutex_);
142 resultListeners_.push_back(callback);
143 locker.unlock();
144
145 if (handler_started != true) {
146 handler_started = true;
147 handler_->SendEvent(DnsQualityEventHandler::MSG_DNS_REPORT_LOOP, report_delay);
148 #if NETSYS_DNS_MONITOR
149 handler_->SendEvent(DnsQualityEventHandler::MSG_DNS_MONITOR_LOOP, monitor_loop_delay);
150 #endif
151 }
152 NETNATIVE_LOG_D("RegisterResultListener, %{public}d", report_delay);
153
154 return 0;
155 }
156
UnregisterResultListener(const sptr<INetDnsResultCallback> & callback)157 int32_t DnsQualityDiag::UnregisterResultListener(const sptr<INetDnsResultCallback> &callback)
158 {
159 std::lock_guard<std::mutex> locker(resultListenersMutex_);
160 resultListeners_.remove(callback);
161 if (resultListeners_.empty()) {
162 handler_started = false;
163 }
164 NETNATIVE_LOG_D("UnregisterResultListener");
165
166 return 0;
167 }
168
RegisterHealthListener(const sptr<INetDnsHealthCallback> & callback)169 int32_t DnsQualityDiag::RegisterHealthListener(const sptr<INetDnsHealthCallback> &callback)
170 {
171 healthListeners_.push_back(callback);
172 handler_started = true;
173 handler_->SendEvent(DnsQualityEventHandler::MSG_DNS_MONITOR_LOOP, monitor_loop_delay);
174
175 return 0;
176 }
177
UnregisterHealthListener(const sptr<INetDnsHealthCallback> & callback)178 int32_t DnsQualityDiag::UnregisterHealthListener(const sptr<INetDnsHealthCallback> &callback)
179 {
180 healthListeners_.remove(callback);
181 if (healthListeners_.empty()) {
182 handler_started = false;
183 }
184
185 return 0;
186 }
187
SetLoopDelay(int32_t delay)188 int32_t DnsQualityDiag::SetLoopDelay(int32_t delay)
189 {
190 monitor_loop_delay = static_cast<uint32_t>(delay);
191 return 0;
192 }
193
query_default_host()194 int32_t DnsQualityDiag::query_default_host()
195 {
196 #if NETSYS_DNS_MONITOR
197 struct addrinfo *res;
198 struct queryparam param;
199 param.qp_type = 1;
200 #endif
201
202 OHOS::NetManagerStandard::NetHandle netHandle;
203 OHOS::NetManagerStandard::NetConnClient::GetInstance().GetDefaultNet(netHandle);
204 int netid = netHandle.GetNetId();
205
206 NETNATIVE_LOG_D("query_default_host: %{public}d, ", netid);
207
208 #if NETSYS_DNS_MONITOR
209 param.qp_netid = netid;
210 getaddrinfo_ext(queryAddr.c_str(), NULL, NULL, &res, ¶m);
211 freeaddrinfo(res);
212 #endif
213
214 return 0;
215 }
216
handle_dns_loop()217 int32_t DnsQualityDiag::handle_dns_loop()
218 {
219 if (handler_started) {
220 if (report_.size() == 0) {
221 query_default_host();
222 }
223 handler_->SendEvent(DnsQualityEventHandler::MSG_DNS_MONITOR_LOOP, monitor_loop_delay);
224 }
225 return 0;
226 }
227
handle_dns_fail()228 int32_t DnsQualityDiag::handle_dns_fail()
229 {
230 if (handler_started) {
231 query_default_host();
232 }
233 return 0;
234 }
235
send_dns_report()236 int32_t DnsQualityDiag::send_dns_report()
237 {
238 if (!handler_started) {
239 report_.clear();
240 return 0;
241 }
242
243 std::unique_lock<std::mutex> locker(resultListenersMutex_);
244 if (report_.size() > 0) {
245 std::list<NetsysNative::NetDnsResultReport> reportSend(report_);
246 report_.clear();
247 NETNATIVE_LOG_D("send_dns_report (%{public}zu)", reportSend.size());
248 for (auto cb: resultListeners_) {
249 NETNATIVE_LOG_D("send_dns_report cb)");
250 cb->OnDnsResultReport(reportSend.size(), reportSend);
251 }
252 }
253 locker.unlock();
254 handler_->SendEvent(DnsQualityEventHandler::MSG_DNS_REPORT_LOOP, report_delay);
255 return 0;
256 }
257
add_dns_report(std::shared_ptr<NetsysNative::NetDnsResultReport> report)258 int32_t DnsQualityDiag::add_dns_report(std::shared_ptr<NetsysNative::NetDnsResultReport> report)
259 {
260 if (!report) {
261 NETNATIVE_LOGE("report is nullptr");
262 return 0;
263 }
264 if (report_.size() < MAX_RESULT_SIZE) {
265 report_.push_back(*report);
266 }
267 return 0;
268 }
269
load_query_addr(const char * defaultAddr)270 int32_t DnsQualityDiag::load_query_addr(const char* defaultAddr)
271 {
272 if (!std::filesystem::exists(URL_CFG_FILE)) {
273 NETNATIVE_LOGE("File not exist (%{public}s)", URL_CFG_FILE);
274 queryAddr = defaultAddr;
275 return 0;
276 }
277
278 std::ifstream file(URL_CFG_FILE);
279 if (!file.is_open()) {
280 NETNATIVE_LOGE("Open file failed (%{public}s)", strerror(errno));
281 queryAddr = defaultAddr;
282 return 0;
283 }
284
285 std::ostringstream oss;
286 oss << file.rdbuf();
287 std::string content = oss.str();
288 auto pos = content.find(DNS_URL_HEADER);
289 if (pos != std::string::npos) {
290 pos += strlen(DNS_URL_HEADER);
291 queryAddr = content.substr(pos, content.find(NEW_LINE_STR, pos) - pos);
292 } else {
293 queryAddr = defaultAddr;
294 }
295 NETNATIVE_LOG_D("Get queryAddr url:[%{public}s]]", queryAddr.c_str());
296
297 return 0;
298 }
299
HandleEvent(const AppExecFwk::InnerEvent::Pointer & event)300 int32_t DnsQualityDiag::HandleEvent(const AppExecFwk::InnerEvent::Pointer &event)
301 {
302 if (!handler_started) {
303 NETNATIVE_LOGI("DnsQualityDiag Handler not started");
304 return 0;
305 }
306 NETNATIVE_LOG_D("DnsQualityDiag Handler event: %{public}d", event->GetInnerEventId());
307
308 switch (event->GetInnerEventId()) {
309 case DnsQualityEventHandler::MSG_DNS_MONITOR_LOOP:
310 handle_dns_loop();
311 break;
312 case DnsQualityEventHandler::MSG_DNS_QUERY_FAIL:
313 handle_dns_fail();
314 break;
315 case DnsQualityEventHandler::MSG_DNS_REPORT_LOOP:
316 send_dns_report();
317 break;
318 case DnsQualityEventHandler::MSG_DNS_NEW_REPORT:
319 auto report = event->GetSharedObject<NetsysNative::NetDnsResultReport>();
320 add_dns_report(report);
321 break;
322 }
323 return 0;
324 }
325 } // namespace OHOS::nmd
326