• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &param);
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