1 /*
2 * Copyright (c) 2023 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 "third_party/musl/include/netdb.h"
21 #include "net_handle.h"
22 #include "net_conn_client.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
DnsQualityDiag()37 DnsQualityDiag::DnsQualityDiag()
38 : defaultNetId_(0),
39 monitor_loop_delay(TIME_DELAY),
40 report_delay(TIME_DELAY),
41 handler_started(false),
42 handler_(nullptr)
43 {
44 InitHandler();
45 load_query_addr(HW_HICLOUD_ADDR);
46 }
47
GetInstance()48 DnsQualityDiag &DnsQualityDiag::GetInstance()
49 {
50 static DnsQualityDiag instance;
51 return instance;
52 }
53
InitHandler()54 int32_t DnsQualityDiag::InitHandler()
55 {
56 if (handler_ == nullptr) {
57 std::shared_ptr<AppExecFwk::EventRunner> runner_ =
58 AppExecFwk::EventRunner::Create(DNS_DIAG_WORK_THREAD);
59 if (!runner_) {
60 NETNATIVE_LOGE("Create net policy work event runner.");
61 } else {
62 handler_ = std::make_shared<DnsQualityEventHandler>(runner_);
63 }
64 }
65 return 0;
66 }
67
SendHealthReport(NetsysNative::NetDnsHealthReport healthreport)68 int32_t DnsQualityDiag::SendHealthReport(NetsysNative::NetDnsHealthReport healthreport)
69 {
70 for (auto cb: healthListeners_) {
71 cb->OnDnsHealthReport(healthreport);
72 }
73
74 return 0;
75 }
ParseReportAddr(uint32_t size,AddrInfo * addrinfo,NetsysNative::NetDnsResultReport & report)76 int32_t DnsQualityDiag::ParseReportAddr(uint32_t size, AddrInfo* addrinfo, NetsysNative::NetDnsResultReport &report)
77 {
78 for (uint8_t i = 0; i < size; i++) {
79 NetsysNative::NetDnsResultAddrInfo ai;
80 AddrInfo *tmp = &(addrinfo[i]);
81 void* addr = NULL;
82 char c_addr[INET6_ADDRSTRLEN];
83 switch (tmp->aiFamily) {
84 case AF_INET:
85 ai.type_ = NetsysNative::ADDR_TYPE_IPV4;
86 addr = &(tmp->aiAddr.sin.sin_addr);
87 break;
88 case AF_INET6:
89 ai.type_ = NetsysNative::ADDR_TYPE_IPV6;
90 addr = &(tmp->aiAddr.sin6.sin6_addr);
91 break;
92 }
93 inet_ntop(tmp->aiFamily, addr, c_addr, sizeof(c_addr));
94 ai.addr_ = c_addr;
95 if (report.addrlist_.size() < MAX_RESULT_SIZE) {
96 report.addrlist_.push_back(ai);
97 } else {
98 break;
99 }
100 }
101 return 0;
102 }
103
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)104 int32_t DnsQualityDiag::ReportDnsResult(uint16_t netId, uint16_t uid, uint32_t pid, int32_t usedtime,
105 char* name, uint32_t size, int32_t failreason, QueryParam queryParam, AddrInfo* addrinfo)
106 {
107 bool reportSizeReachLimit = (report_.size() >= MAX_RESULT_SIZE);
108
109 NETNATIVE_LOG_D("ReportDnsResult: %{public}d, %{public}d, %{public}d, %{public}d, %{public}d, %{public}d",
110 netId, uid, pid, usedtime, size, failreason);
111
112 if (!reportSizeReachLimit) {
113 NetsysNative::NetDnsResultReport report;
114 report.netid_ = netId;
115 report.uid_ = uid;
116 report.pid_ = pid;
117 report.timeused_ = usedtime;
118 report.queryresult_ = failreason;
119 report.host_ = name;
120 if (failreason == 0) {
121 ParseReportAddr(size, addrinfo, report);
122 }
123 NETNATIVE_LOG_D("ReportDnsResult: %{public}s", report.host_.c_str());
124 std::shared_ptr<NetsysNative::NetDnsResultReport> rpt =
125 std::make_shared<NetsysNative::NetDnsResultReport>(report);
126 auto event = AppExecFwk::InnerEvent::Get(DnsQualityEventHandler::MSG_DNS_NEW_REPORT, rpt);
127 handler_->SendEvent(event);
128 }
129
130 return 0;
131 }
132
RegisterResultListener(const sptr<INetDnsResultCallback> & callback,uint32_t timeStep)133 int32_t DnsQualityDiag::RegisterResultListener(const sptr<INetDnsResultCallback> &callback, uint32_t timeStep)
134 {
135 report_delay = std::max(report_delay, timeStep);
136 resultListeners_.push_back(callback);
137
138 if (handler_started != true) {
139 handler_started = true;
140 handler_->SendEvent(DnsQualityEventHandler::MSG_DNS_REPORT_LOOP, report_delay);
141 #if NETSYS_DNS_MONITOR
142 handler_->SendEvent(DnsQualityEventHandler::MSG_DNS_MONITOR_LOOP, monitor_loop_delay);
143 #endif
144 }
145 NETNATIVE_LOG_D("RegisterResultListener, %{public}d", report_delay);
146
147 return 0;
148 }
149
UnregisterResultListener(const sptr<INetDnsResultCallback> & callback)150 int32_t DnsQualityDiag::UnregisterResultListener(const sptr<INetDnsResultCallback> &callback)
151 {
152 resultListeners_.remove(callback);
153 if (resultListeners_.empty()) {
154 handler_started = false;
155 }
156 NETNATIVE_LOG_D("UnregisterResultListener");
157
158 return 0;
159 }
160
RegisterHealthListener(const sptr<INetDnsHealthCallback> & callback)161 int32_t DnsQualityDiag::RegisterHealthListener(const sptr<INetDnsHealthCallback> &callback)
162 {
163 healthListeners_.push_back(callback);
164 handler_started = true;
165 handler_->SendEvent(DnsQualityEventHandler::MSG_DNS_MONITOR_LOOP, monitor_loop_delay);
166
167 return 0;
168 }
169
UnregisterHealthListener(const sptr<INetDnsHealthCallback> & callback)170 int32_t DnsQualityDiag::UnregisterHealthListener(const sptr<INetDnsHealthCallback> &callback)
171 {
172 healthListeners_.remove(callback);
173 if (healthListeners_.empty()) {
174 handler_started = false;
175 }
176
177 return 0;
178 }
179
SetLoopDelay(int32_t delay)180 int32_t DnsQualityDiag::SetLoopDelay(int32_t delay)
181 {
182 monitor_loop_delay = delay;
183 return 0;
184 }
185
query_default_host()186 int32_t DnsQualityDiag::query_default_host()
187 {
188 #if NETSYS_DNS_MONITOR
189 struct addrinfo *res;
190 struct queryparam param;
191 param.qp_type = 1;
192 #endif
193
194 OHOS::NetManagerStandard::NetHandle netHandle;
195 OHOS::NetManagerStandard::NetConnClient::GetInstance().GetDefaultNet(netHandle);
196 int netid = netHandle.GetNetId();
197
198 NETNATIVE_LOG_D("query_default_host: %{public}d, ", netid);
199
200 #if NETSYS_DNS_MONITOR
201 param.qp_netid = netid;
202 getaddrinfo_ext(queryAddr.c_str(), NULL, NULL, &res, ¶m);
203 freeaddrinfo(res);
204 #endif
205
206 return 0;
207 }
208
handle_dns_loop()209 int32_t DnsQualityDiag::handle_dns_loop()
210 {
211 if (handler_started) {
212 if (report_.size() == 0) {
213 query_default_host();
214 }
215 handler_->SendEvent(DnsQualityEventHandler::MSG_DNS_MONITOR_LOOP, monitor_loop_delay);
216 }
217 return 0;
218 }
219
handle_dns_fail()220 int32_t DnsQualityDiag::handle_dns_fail()
221 {
222 if (handler_started) {
223 query_default_host();
224 }
225 return 0;
226 }
227
send_dns_report()228 int32_t DnsQualityDiag::send_dns_report()
229 {
230 if (!handler_started) {
231 report_.clear();
232 return 0;
233 }
234
235 if (report_.size() > 0) {
236 std::list<NetsysNative::NetDnsResultReport> reportSend(report_);
237 report_.clear();
238 NETNATIVE_LOG_D("send_dns_report (%{public}u)", reportSend.size());
239 for (auto cb: resultListeners_) {
240 NETNATIVE_LOGI("send_dns_report cb)");
241 cb->OnDnsResultReport(reportSend.size(), reportSend);
242 }
243 }
244 handler_->SendEvent(DnsQualityEventHandler::MSG_DNS_REPORT_LOOP, report_delay);
245 return 0;
246 }
247
add_dns_report(std::shared_ptr<NetsysNative::NetDnsResultReport> report)248 int32_t DnsQualityDiag::add_dns_report(std::shared_ptr<NetsysNative::NetDnsResultReport> report)
249 {
250 NETNATIVE_LOGI("add_dns_report (%{public}s)", report->host_.c_str());
251 if (report_.size() < MAX_RESULT_SIZE) {
252 report_.push_back(*report);
253 }
254 return 0;
255 }
256
load_query_addr(const char * defaultAddr)257 int32_t DnsQualityDiag::load_query_addr(const char* defaultAddr)
258 {
259 if (!std::filesystem::exists(URL_CFG_FILE)) {
260 NETNATIVE_LOGE("File not exist (%{public}s)", URL_CFG_FILE);
261 queryAddr = defaultAddr;
262 return 0;
263 }
264
265 std::ifstream file(URL_CFG_FILE);
266 if (!file.is_open()) {
267 NETNATIVE_LOGE("Open file failed (%{public}s)", strerror(errno));
268 queryAddr = defaultAddr;
269 return 0;
270 }
271
272 std::ostringstream oss;
273 oss << file.rdbuf();
274 std::string content = oss.str();
275 auto pos = content.find(DNS_URL_HEADER);
276 if (pos != std::string::npos) {
277 pos += strlen(DNS_URL_HEADER);
278 queryAddr = content.substr(pos, content.find(NEW_LINE_STR, pos) - pos);
279 } else {
280 queryAddr = defaultAddr;
281 }
282 NETNATIVE_LOG_D("Get queryAddr url:[%{public}s]]", queryAddr.c_str());
283
284 return 0;
285 }
286
HandleEvent(const AppExecFwk::InnerEvent::Pointer & event)287 int32_t DnsQualityDiag::HandleEvent(const AppExecFwk::InnerEvent::Pointer &event)
288 {
289 if (!handler_started) {
290 NETNATIVE_LOGI("DnsQualityDiag Handler not started");
291 return 0;
292 }
293 NETNATIVE_LOG_D("DnsQualityDiag Handler event: %{public}d", event->GetInnerEventId());
294
295 switch (event->GetInnerEventId()) {
296 case DnsQualityEventHandler::MSG_DNS_MONITOR_LOOP:
297 handle_dns_loop();
298 break;
299 case DnsQualityEventHandler::MSG_DNS_QUERY_FAIL:
300 handle_dns_fail();
301 break;
302 case DnsQualityEventHandler::MSG_DNS_REPORT_LOOP:
303 send_dns_report();
304 break;
305 case DnsQualityEventHandler::MSG_DNS_NEW_REPORT:
306 auto report = event->GetSharedObject<NetsysNative::NetDnsResultReport>();
307 add_dns_report(report);
308 break;
309 }
310 return 0;
311 }
312 } // namespace OHOS::nmd
313