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_param_cache.h"
20 #include "dns_quality_diag.h"
21 #include "net_conn_client.h"
22 #include "net_handle.h"
23
24 namespace OHOS::nmd {
25 namespace {
26 using namespace OHOS::NetsysNative;
27 }
28
29 const char *DNS_DIAG_WORK_THREAD = "DNS_DIAG_WORK_THREAD";
30 const char *HW_HICLOUD_ADDR = "connectivitycheck.platform.hicloud.com";
31 const uint32_t MAX_RESULT_SIZE = 32;
32 const char *URL_CFG_FILE = "/system/etc/netdetectionurl.conf";
33 const char *DNS_URL_HEADER = "DnsProbeUrl:";
34 const char NEW_LINE_STR = '\n';
35 const uint32_t TIME_DELAY = 500;
36 constexpr const uint32_t DNS_ABNORMAL_REPORT_INTERVAL = 2;
37
DnsQualityDiag()38 DnsQualityDiag::DnsQualityDiag()
39 : defaultNetId_(0),
40 monitor_loop_delay(TIME_DELAY),
41 report_delay(TIME_DELAY),
42 handler_started(false),
43 handler_(nullptr)
44 {
45 InitHandler();
46 load_query_addr(HW_HICLOUD_ADDR);
47 }
48
GetInstance()49 DnsQualityDiag &DnsQualityDiag::GetInstance()
50 {
51 static DnsQualityDiag instance;
52 return instance;
53 }
54
InitHandler()55 int32_t DnsQualityDiag::InitHandler()
56 {
57 if (handler_ == nullptr) {
58 std::shared_ptr<AppExecFwk::EventRunner> runner_ =
59 AppExecFwk::EventRunner::Create(DNS_DIAG_WORK_THREAD);
60 if (!runner_) {
61 NETNATIVE_LOGE("Create net policy work event runner.");
62 } else {
63 handler_ = std::make_shared<DnsQualityEventHandler>(runner_);
64 }
65 }
66 return 0;
67 }
68
SendHealthReport(NetsysNative::NetDnsHealthReport healthreport)69 int32_t DnsQualityDiag::SendHealthReport(NetsysNative::NetDnsHealthReport healthreport)
70 {
71 for (auto cb: healthListeners_) {
72 cb->OnDnsHealthReport(healthreport);
73 }
74
75 return 0;
76 }
ParseReportAddr(uint32_t size,AddrInfo * addrinfo,NetsysNative::NetDnsResultReport & report)77 int32_t DnsQualityDiag::ParseReportAddr(uint32_t size, AddrInfo* addrinfo, NetsysNative::NetDnsResultReport &report)
78 {
79 for (uint8_t i = 0; i < size; i++) {
80 NetsysNative::NetDnsResultAddrInfo ai;
81 AddrInfo *tmp = &(addrinfo[i]);
82 void* addr = NULL;
83 char c_addr[INET6_ADDRSTRLEN];
84 switch (tmp->aiFamily) {
85 case AF_INET:
86 ai.type_ = NetsysNative::ADDR_TYPE_IPV4;
87 addr = &(tmp->aiAddr.sin.sin_addr);
88 break;
89 case AF_INET6:
90 ai.type_ = NetsysNative::ADDR_TYPE_IPV6;
91 addr = &(tmp->aiAddr.sin6.sin6_addr);
92 break;
93 }
94 inet_ntop(tmp->aiFamily, addr, c_addr, sizeof(c_addr));
95 ai.addr_ = c_addr;
96 if (report.addrlist_.size() < MAX_RESULT_SIZE) {
97 report.addrlist_.push_back(ai);
98 } else {
99 break;
100 }
101 }
102 return 0;
103 }
104
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)105 int32_t DnsQualityDiag::ReportDnsResult(uint16_t netId, uint16_t uid, uint32_t pid, int32_t usedtime,
106 char* name, uint32_t size, int32_t failreason, QueryParam queryParam, AddrInfo* addrinfo)
107 {
108 bool reportSizeReachLimit = (report_.size() >= MAX_RESULT_SIZE);
109
110 NETNATIVE_LOG_D("ReportDnsResult: %{public}d, %{public}d, %{public}d, %{public}d, %{public}d, %{public}d",
111 netId, uid, pid, usedtime, size, failreason);
112
113 if (queryParam.type == 1) {
114 NETNATIVE_LOG_D("ReportDnsResult: query from Netmanager ignore report");
115 return 0;
116 }
117
118 if (!reportSizeReachLimit) {
119 NetsysNative::NetDnsResultReport report;
120 report.netid_ = netId;
121 report.uid_ = uid;
122 report.pid_ = pid;
123 report.timeused_ = static_cast<uint32_t>(usedtime);
124 report.queryresult_ = static_cast<uint32_t>(failreason);
125 report.host_ = name;
126 if (failreason == 0) {
127 ParseReportAddr(size, addrinfo, report);
128 }
129 std::shared_ptr<NetsysNative::NetDnsResultReport> rpt =
130 std::make_shared<NetsysNative::NetDnsResultReport>(report);
131 auto event = AppExecFwk::InnerEvent::Get(DnsQualityEventHandler::MSG_DNS_NEW_REPORT, rpt);
132 handler_->SendEvent(event);
133 }
134
135 return 0;
136 }
137
GetDefaultDnsServerList(int32_t uid,std::vector<std::string> & servers)138 void DnsQualityDiag::GetDefaultDnsServerList(int32_t uid, std::vector<std::string> &servers)
139 {
140 int32_t netId = DnsParamCache::GetInstance().GetDefaultNetwork();
141 std::vector<std::string> domains;
142 uint16_t baseTimeoutMsec;
143 uint8_t retryCount;
144 DnsParamCache::GetInstance().GetResolverConfig(
145 netId, uid, servers, domains, baseTimeoutMsec, retryCount);
146 }
147
FillDnsQueryResultReport(NetsysNative::NetDnsQueryResultReport & report,PostDnsQueryParam & queryParam)148 void DnsQualityDiag::FillDnsQueryResultReport(NetsysNative::NetDnsQueryResultReport &report,
149 PostDnsQueryParam &queryParam)
150 {
151 uint16_t resBitInfo = 0;
152 report.uid_ = queryParam.uid;
153 report.pid_ = queryParam.pid;
154 report.addrSize_ = queryParam.addrSize;
155 DnsProcessInfoExt processInfo = queryParam.processInfo;
156 report.sourceFrom_ = processInfo.sourceFrom;
157 std::string srcAddr(processInfo.srcAddr);
158 report.srcAddr_ = srcAddr;
159 std::vector<std::string> serverList;
160 GetDefaultDnsServerList(queryParam.uid, serverList);
161 report.dnsServerSize_ = static_cast<uint8_t>(serverList.size());
162 report.dnsServerList_ = serverList;
163 report.queryTime_ = static_cast<uint64_t>(processInfo.queryTime);
164 report.host_ = processInfo.hostname;
165 report.retCode_ = processInfo.retCode;
166 report.firstQueryEndDuration_ = processInfo.firstQueryEndDuration;
167 report.firstQueryEnd2AppDuration_ = processInfo.firstQueryEnd2AppDuration;
168 report.ipv4RetCode_ = processInfo.ipv4QueryInfo.retCode;
169 std::string ipv4ServerName(processInfo.ipv4QueryInfo.serverAddr);
170 report.ipv4ServerName_ = ipv4ServerName;
171 report.ipv6RetCode_ = processInfo.ipv6QueryInfo.retCode;
172 std::string ipv6ServerName(processInfo.ipv6QueryInfo.serverAddr);
173 report.ipv6ServerName_ = ipv6ServerName;
174 resBitInfo |= (DnsParamCache::GetInstance().IsUseVpnDns(queryParam.uid) ? VPN_NET_FLAG : 0);
175 resBitInfo |= (processInfo.isFromCache ? FROM_CACHE_FLAG : 0);
176 resBitInfo |= (processInfo.ipv4QueryInfo.isNoAnswer ? IPV4_NO_ANSWER_FLAG : 0);
177 resBitInfo |= (processInfo.ipv4QueryInfo.cname ? IPV4_CNAME_FLAG : 0);
178 resBitInfo |= (processInfo.ipv6QueryInfo.isNoAnswer ? IPV6_NO_ANSWER_FLAG : 0);
179 resBitInfo |= (processInfo.ipv6QueryInfo.cname ? IPV6_CNAME_FLAG : 0);
180 report.resBitInfo_ = resBitInfo;
181 }
182
ParseDnsQueryReportAddr(uint8_t size,AddrInfo * addrinfo,NetsysNative::NetDnsQueryResultReport & report)183 int32_t DnsQualityDiag::ParseDnsQueryReportAddr(uint8_t size,
184 AddrInfo* addrinfo, NetsysNative::NetDnsQueryResultReport &report)
185 {
186 for (uint8_t i = 0; i < size; i++) {
187 NetsysNative::NetDnsQueryResultAddrInfo ai;
188 AddrInfo *tmp = &(addrinfo[i]);
189 void* addr = NULL;
190 char c_addr[INET6_ADDRSTRLEN];
191 switch (tmp->aiFamily) {
192 case AF_INET:
193 ai.type_ = NetsysNative::ADDR_TYPE_IPV4;
194 addr = &(tmp->aiAddr.sin.sin_addr);
195 break;
196 case AF_INET6:
197 ai.type_ = NetsysNative::ADDR_TYPE_IPV6;
198 addr = &(tmp->aiAddr.sin6.sin6_addr);
199 break;
200 }
201 inet_ntop(tmp->aiFamily, addr, c_addr, sizeof(c_addr));
202 ai.addr_ = c_addr;
203 if (report.addrlist_.size() < MAX_RESULT_SIZE) {
204 report.addrlist_.push_back(ai);
205 } else {
206 break;
207 }
208 }
209 return 0;
210 }
211
ReportDnsQueryResult(PostDnsQueryParam queryParam,AddrInfo * addrinfo,uint8_t addrSize)212 int32_t DnsQualityDiag::ReportDnsQueryResult(PostDnsQueryParam queryParam, AddrInfo* addrinfo, uint8_t addrSize)
213 {
214 bool reportSizeReachLimit = (dnsQueryReport_.size() >= MAX_RESULT_SIZE);
215 if (!reportSizeReachLimit) {
216 NetsysNative::NetDnsQueryResultReport report;
217 FillDnsQueryResultReport(report, queryParam);
218 if (addrSize > 0 && addrinfo != nullptr) {
219 ParseDnsQueryReportAddr(addrSize, addrinfo, report);
220 }
221 std::shared_ptr<NetsysNative::NetDnsQueryResultReport> rpt =
222 std::make_shared<NetsysNative::NetDnsQueryResultReport>(report);
223 auto event = AppExecFwk::InnerEvent::Get(DnsQualityEventHandler::MSG_DNS_QUERY_RESULT_REPORT, rpt);
224 handler_->SendEvent(event);
225 }
226
227 return 0;
228 }
229
ReportDnsQueryAbnormal(uint32_t eventfailcause,PostDnsQueryParam queryParam,AddrInfo * addrinfo)230 int32_t DnsQualityDiag::ReportDnsQueryAbnormal(uint32_t eventfailcause, PostDnsQueryParam queryParam,
231 AddrInfo* addrinfo)
232 {
233 std::unique_lock<std::mutex> locker(dnsAbnormalTimeMutex_);
234 uint32_t timeNow = static_cast<uint32_t>(time(NULL));
235 if (timeNow - last_dns_abnormal_report_time < DNS_ABNORMAL_REPORT_INTERVAL) {
236 locker.unlock();
237 return 0;
238 }
239 last_dns_abnormal_report_time = static_cast<uint32_t>(time(NULL));
240 locker.unlock();
241 NetsysNative::NetDnsQueryResultReport report;
242 FillDnsQueryResultReport(report, queryParam);
243 if (queryParam.addrSize > 0 && addrinfo != nullptr) {
244 ParseDnsQueryReportAddr(queryParam.addrSize, addrinfo, report);
245 }
246 std::shared_ptr<DnsAbnormalInfo> rpt = std::make_shared<DnsAbnormalInfo>();
247 rpt->eventfailcause = eventfailcause;
248 rpt->report = report;
249 auto event = AppExecFwk::InnerEvent::Get(DnsQualityEventHandler::MSG_DNS_QUERY_ABNORMAL_REPORT, rpt);
250 handler_->SendEvent(event);
251 return 0;
252 }
253
RegisterResultListener(const sptr<INetDnsResultCallback> & callback,uint32_t timeStep)254 int32_t DnsQualityDiag::RegisterResultListener(const sptr<INetDnsResultCallback> &callback, uint32_t timeStep)
255 {
256 report_delay = std::max(report_delay, timeStep);
257 if (callback == nullptr) {
258 NETNATIVE_LOGE("callback is nullptr");
259 return 0;
260 }
261
262 std::unique_lock<std::mutex> locker(resultListenersMutex_);
263 std::list<sptr<NetsysNative::INetDnsResultCallback>>::iterator iter;
264 for (iter = resultListeners_.begin(); iter != resultListeners_.end(); ++iter) {
265 if ((*iter)->AsObject().GetRefPtr() == callback->AsObject().GetRefPtr()) {
266 NETNATIVE_LOGI("callback is already registered");
267 break;
268 }
269 }
270 if (iter == resultListeners_.end()) {
271 resultListeners_.push_back(callback);
272 }
273 locker.unlock();
274
275 if (handler_started != true) {
276 handler_started = true;
277 handler_->SendEvent(DnsQualityEventHandler::MSG_DNS_REPORT_LOOP, report_delay);
278 #if NETSYS_DNS_MONITOR
279 handler_->SendEvent(DnsQualityEventHandler::MSG_DNS_MONITOR_LOOP, monitor_loop_delay);
280 #endif
281 }
282 NETNATIVE_LOG_D("RegisterResultListener, %{public}d", report_delay);
283
284 return 0;
285 }
286
UnregisterResultListener(const sptr<INetDnsResultCallback> & callback)287 int32_t DnsQualityDiag::UnregisterResultListener(const sptr<INetDnsResultCallback> &callback)
288 {
289 if (callback == nullptr) {
290 NETNATIVE_LOGE("callback is nullptr");
291 return 0;
292 }
293 std::lock_guard<std::mutex> locker(resultListenersMutex_);
294 auto iter = resultListeners_.begin();
295 while (iter != resultListeners_.end()) {
296 if ((*iter)->AsObject().GetRefPtr() == callback->AsObject().GetRefPtr()) {
297 iter = resultListeners_.erase(iter);
298 } else {
299 ++iter;
300 }
301 }
302
303 if (resultListeners_.empty()) {
304 handler_started = false;
305 }
306 NETNATIVE_LOG_D("UnregisterResultListener");
307
308 return 0;
309 }
310
RegisterHealthListener(const sptr<INetDnsHealthCallback> & callback)311 int32_t DnsQualityDiag::RegisterHealthListener(const sptr<INetDnsHealthCallback> &callback)
312 {
313 healthListeners_.push_back(callback);
314 handler_started = true;
315 handler_->SendEvent(DnsQualityEventHandler::MSG_DNS_MONITOR_LOOP, monitor_loop_delay);
316
317 return 0;
318 }
319
UnregisterHealthListener(const sptr<INetDnsHealthCallback> & callback)320 int32_t DnsQualityDiag::UnregisterHealthListener(const sptr<INetDnsHealthCallback> &callback)
321 {
322 healthListeners_.remove(callback);
323 if (healthListeners_.empty()) {
324 handler_started = false;
325 }
326
327 return 0;
328 }
329
SetLoopDelay(int32_t delay)330 int32_t DnsQualityDiag::SetLoopDelay(int32_t delay)
331 {
332 monitor_loop_delay = static_cast<uint32_t>(delay);
333 return 0;
334 }
335
query_default_host()336 int32_t DnsQualityDiag::query_default_host()
337 {
338 #if NETSYS_DNS_MONITOR
339 struct addrinfo *res;
340 struct queryparam param;
341 param.qp_type = 1;
342 #endif
343
344 OHOS::NetManagerStandard::NetHandle netHandle;
345 OHOS::NetManagerStandard::NetConnClient::GetInstance().GetDefaultNet(netHandle);
346 int netid = netHandle.GetNetId();
347
348 NETNATIVE_LOG_D("query_default_host: %{public}d, ", netid);
349
350 #if NETSYS_DNS_MONITOR
351 param.qp_netid = netid;
352 getaddrinfo_ext(queryAddr.c_str(), NULL, NULL, &res, ¶m);
353 freeaddrinfo(res);
354 #endif
355
356 return 0;
357 }
358
handle_dns_loop()359 int32_t DnsQualityDiag::handle_dns_loop()
360 {
361 if (handler_started) {
362 if (report_.size() == 0) {
363 query_default_host();
364 }
365 handler_->SendEvent(DnsQualityEventHandler::MSG_DNS_MONITOR_LOOP, monitor_loop_delay);
366 }
367 return 0;
368 }
369
handle_dns_fail()370 int32_t DnsQualityDiag::handle_dns_fail()
371 {
372 if (handler_started) {
373 query_default_host();
374 }
375 return 0;
376 }
377
send_dns_report()378 int32_t DnsQualityDiag::send_dns_report()
379 {
380 if (!handler_started) {
381 report_.clear();
382 return 0;
383 }
384
385 std::unique_lock<std::mutex> locker(resultListenersMutex_);
386 if (report_.size() > 0) {
387 std::list<NetsysNative::NetDnsResultReport> reportSend(report_);
388 report_.clear();
389 NETNATIVE_LOG_D("send_dns_report (%{public}zu)", reportSend.size());
390 for (auto cb: resultListeners_) {
391 NETNATIVE_LOG_D("send_dns_report cb)");
392 cb->OnDnsResultReport(reportSend.size(), reportSend);
393 }
394 }
395 if (dnsQueryReport_.size() > 0) {
396 std::list<NetsysNative::NetDnsQueryResultReport> reportSend(dnsQueryReport_);
397 dnsQueryReport_.clear();
398 for (auto cb: resultListeners_) {
399 cb->OnDnsQueryResultReport(reportSend.size(), reportSend);
400 }
401 }
402 locker.unlock();
403 handler_->SendEvent(DnsQualityEventHandler::MSG_DNS_REPORT_LOOP, report_delay);
404 return 0;
405 }
406
add_dns_query_report(std::shared_ptr<NetsysNative::NetDnsQueryResultReport> report)407 int32_t DnsQualityDiag::add_dns_query_report(std::shared_ptr<NetsysNative::NetDnsQueryResultReport> report)
408 {
409 if (!report) {
410 return 0;
411 }
412 if (dnsQueryReport_.size() < MAX_RESULT_SIZE) {
413 dnsQueryReport_.push_back(*report);
414 }
415 return 0;
416 }
417
handle_dns_abnormal(std::shared_ptr<DnsAbnormalInfo> abnormalInfo)418 int32_t DnsQualityDiag::handle_dns_abnormal(std::shared_ptr<DnsAbnormalInfo> abnormalInfo)
419 {
420 if (!abnormalInfo) {
421 return 0;
422 }
423 for (auto cb: resultListeners_) {
424 cb->OnDnsQueryAbnormalReport(abnormalInfo->eventfailcause, abnormalInfo->report);
425 }
426 return 0;
427 }
428
add_dns_report(std::shared_ptr<NetsysNative::NetDnsResultReport> report)429 int32_t DnsQualityDiag::add_dns_report(std::shared_ptr<NetsysNative::NetDnsResultReport> report)
430 {
431 if (!report) {
432 NETNATIVE_LOGE("report is nullptr");
433 return 0;
434 }
435 if (report_.size() < MAX_RESULT_SIZE) {
436 report_.push_back(*report);
437 }
438 return 0;
439 }
440
load_query_addr(const char * defaultAddr)441 int32_t DnsQualityDiag::load_query_addr(const char* defaultAddr)
442 {
443 if (!std::filesystem::exists(URL_CFG_FILE)) {
444 NETNATIVE_LOGE("File not exist (%{public}s)", URL_CFG_FILE);
445 queryAddr = defaultAddr;
446 return 0;
447 }
448
449 std::ifstream file(URL_CFG_FILE);
450 if (!file.is_open()) {
451 NETNATIVE_LOGE("Open file failed (%{public}s)", strerror(errno));
452 queryAddr = defaultAddr;
453 return 0;
454 }
455
456 std::ostringstream oss;
457 oss << file.rdbuf();
458 std::string content = oss.str();
459 auto pos = content.find(DNS_URL_HEADER);
460 if (pos != std::string::npos) {
461 pos += strlen(DNS_URL_HEADER);
462 queryAddr = content.substr(pos, content.find(NEW_LINE_STR, pos) - pos);
463 } else {
464 queryAddr = defaultAddr;
465 }
466 NETNATIVE_LOG_D("Get queryAddr url:[%{public}s]]", queryAddr.c_str());
467
468 return 0;
469 }
470
HandleEvent(const AppExecFwk::InnerEvent::Pointer & event)471 int32_t DnsQualityDiag::HandleEvent(const AppExecFwk::InnerEvent::Pointer &event)
472 {
473 if (!handler_started) {
474 NETNATIVE_LOGI("DnsQualityDiag Handler not started");
475 return 0;
476 }
477 NETNATIVE_LOG_D("DnsQualityDiag Handler event: %{public}d", event->GetInnerEventId());
478
479 switch (event->GetInnerEventId()) {
480 case DnsQualityEventHandler::MSG_DNS_MONITOR_LOOP:
481 handle_dns_loop();
482 break;
483 case DnsQualityEventHandler::MSG_DNS_QUERY_FAIL:
484 handle_dns_fail();
485 break;
486 case DnsQualityEventHandler::MSG_DNS_REPORT_LOOP:
487 send_dns_report();
488 break;
489 case DnsQualityEventHandler::MSG_DNS_NEW_REPORT:
490 {
491 auto report = event->GetSharedObject<NetsysNative::NetDnsResultReport>();
492 add_dns_report(report);
493 }
494 break;
495 case DnsQualityEventHandler::MSG_DNS_QUERY_RESULT_REPORT:
496 {
497 auto queryReport = event->GetSharedObject<NetsysNative::NetDnsQueryResultReport>();
498 add_dns_query_report(queryReport);
499 }
500 break;
501 case DnsQualityEventHandler::MSG_DNS_QUERY_ABNORMAL_REPORT:
502 {
503 auto queryReport = event->GetSharedObject<DnsAbnormalInfo>();
504 handle_dns_abnormal(queryReport);
505 }
506 break;
507 }
508 return 0;
509 }
510 } // namespace OHOS::nmd
511