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, &param);
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