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 "net_http_probe.h"
17 
18 #include <cerrno>
19 #include <memory>
20 #include <numeric>
21 #include <unistd.h>
22 
23 #include "fwmark_client.h"
24 #include "netsys_controller.h"
25 #include "net_manager_constants.h"
26 #include "net_mgr_log_wrapper.h"
27 #include "net_proxy_userinfo.h"
28 #include "netmanager_base_common_utils.h"
29 
30 #define NETPROBE_CURL_EASY_SET_OPTION(handle, opt, data)                                                     \
31     do {                                                                                                     \
32         CURLcode result = curl_easy_setopt(handle, opt, data);                                               \
33         if (result != CURLE_OK) {                                                                            \
34             const char *err = curl_easy_strerror(result);                                                    \
35             NETMGR_LOG_E("Failed to set curl option: %{public}s, %{public}s %{public}d", #opt, err, result); \
36             return false;                                                                                    \
37         }                                                                                                    \
38     } while (0)
39 
40 namespace OHOS {
41 namespace NetManagerStandard {
42 namespace {
43 constexpr int PERFORM_POLL_INTERVAL_MS = 50;
44 constexpr int64_t HTTP_OK_CODE = 200;
45 constexpr int32_t DEFAULT_CONTENT_LENGTH_VALUE = -1;
46 constexpr int32_t MIN_VALID_CONTENT_LENGTH_VALUE = 5;
47 constexpr int32_t FAIL_CODE = 599;
48 constexpr int32_t PORTAL_CODE = 302;
49 constexpr int32_t HTTP_RES_CODE_BAD_REQUEST = 400;
50 constexpr int32_t HTTP_RES_CODE_CLIENT_ERRORS_MAX = 499;
51 constexpr int CURL_CONNECT_TIME_OUT_MS = 10000;
52 constexpr int CURL_OPERATE_TIME_OUT_MS = 10000;
53 constexpr int32_t DOMAIN_IP_ADDR_LEN_MAX = 128;
54 constexpr int32_t DEFAULT_HTTP_PORT = 80;
55 constexpr int32_t DEFAULT_HTTPS_PORT = 443;
56 constexpr const char *ADDR_SEPARATOR = ",";
57 constexpr const char *SYMBOL_COLON = ":";
58 const std::string DEFAULT_USER_AGENT = std::string("User-Agent: Mozilla/5.0 (X11; Linux x86_64) ") +
59     std::string("AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.32 Safari/537.36");
60 constexpr const char *CONNECTION_PROPERTY = "Connection: close";
61 constexpr const char *ACCEPT_ENCODING = "Accept-Encoding: gzip";
62 const std::string CONNECTION_CLOSE_VALUE = "close";
63 const std::string CONNECTION_KEY = "Connection:";
64 const std::string CONTENT_LENGTH_KEY = "Content-Length:";
65 const std::string KEY_WORDS_REDIRECTION = "location.replace";
66 const std::string HTML_TITLE_HTTP_EN = "http://";
67 const std::string HTML_TITLE_HTTPS_EN = "https://";
68 constexpr const char NEW_LINE_STR = '\n';
69 } // namespace
70 
71 std::mutex NetHttpProbe::initCurlMutex_;
72 int32_t NetHttpProbe::useCurlCount_ = 0;
CurlGlobalInit()73 bool NetHttpProbe::CurlGlobalInit()
74 {
75     NETMGR_LOG_D("curl_global_init() in");
76     std::lock_guard<std::mutex> lock(initCurlMutex_);
77     if (useCurlCount_ == 0) {
78         NETMGR_LOG_D("Call curl_global_init()");
79         if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
80             NETMGR_LOG_E("curl_global_init() failed");
81             return false;
82         }
83     }
84     useCurlCount_++;
85     NETMGR_LOG_D("curl_global_init() count:[%{public}d]", useCurlCount_);
86     return true;
87 }
88 
CurlGlobalCleanup()89 void NetHttpProbe::CurlGlobalCleanup()
90 {
91     NETMGR_LOG_D("CurlGlobalCleanup() in");
92     std::lock_guard<std::mutex> lock(initCurlMutex_);
93     useCurlCount_ = useCurlCount_ > 0 ? (useCurlCount_ - 1) : 0;
94     NETMGR_LOG_D("Curl global used count remain:[%{public}d]", useCurlCount_);
95     if (useCurlCount_ == 0) {
96         NETMGR_LOG_D("Call curl_global_cleanup()");
97         curl_global_cleanup();
98     }
99 }
100 
NetHttpProbe(uint32_t netId,NetBearType bearType,const NetLinkInfo & netLinkInfo,ProbeType probeType)101 NetHttpProbe::NetHttpProbe(uint32_t netId, NetBearType bearType, const NetLinkInfo &netLinkInfo, ProbeType probeType)
102     : netId_(netId), netBearType_(bearType), netLinkInfo_(netLinkInfo), probeType_(probeType)
103 {
104     isCurlInit_ = NetHttpProbe::CurlGlobalInit();
105 }
106 
~NetHttpProbe()107 NetHttpProbe::~NetHttpProbe()
108 {
109     NetHttpProbe::CurlGlobalCleanup();
110     isCurlInit_ = false;
111 }
112 
SendProbe(ProbeType probeType,const std::string & httpUrl,const std::string & httpsUrl)113 int32_t NetHttpProbe::SendProbe(ProbeType probeType, const std::string &httpUrl, const std::string &httpsUrl)
114 {
115     NETMGR_LOG_I("Send net:[%{public}d] %{public}s probe in", netId_,
116         ((IsHttpsDetect(probeType)) ? "https" : "http"));
117     ClearProbeResult();
118     if (!CheckCurlGlobalInitState()) {
119         return NETMANAGER_ERR_INTERNAL;
120     }
121 
122     if (!InitHttpCurl(probeType)) {
123         CleanHttpCurl();
124         return NETMANAGER_ERR_INTERNAL;
125     }
126 
127     if (!SetCurlOptions(probeType, httpUrl, httpsUrl)) {
128         NETMGR_LOG_E("Set http/https probe options failed");
129         CleanHttpCurl();
130         return NETMANAGER_ERR_INTERNAL;
131     }
132 
133     SendHttpProbeRequest();
134     RecvHttpProbeResponse();
135     CleanHttpCurl();
136     if (!defaultUseGlobalHttpProxy_) {
137         defaultUseGlobalHttpProxy_ = true;
138     }
139     return NETMANAGER_SUCCESS;
140 }
141 
GetHttpProbeResult() const142 NetHttpProbeResult NetHttpProbe::GetHttpProbeResult() const
143 {
144     return httpProbeResult_;
145 }
146 
GetHttpsProbeResult() const147 NetHttpProbeResult NetHttpProbe::GetHttpsProbeResult() const
148 {
149     return httpsProbeResult_;
150 }
151 
UpdateNetLinkInfo(const NetLinkInfo & netLinkInfo)152 void NetHttpProbe::UpdateNetLinkInfo(const NetLinkInfo &netLinkInfo)
153 {
154     netLinkInfo_ = netLinkInfo;
155 }
156 
UpdateGlobalHttpProxy(const HttpProxy & httpProxy)157 void NetHttpProbe::UpdateGlobalHttpProxy(const HttpProxy &httpProxy)
158 {
159     std::lock_guard<std::mutex> locker(proxyMtx_);
160     globalHttpProxy_ = httpProxy;
161 }
162 
CheckCurlGlobalInitState()163 bool NetHttpProbe::CheckCurlGlobalInitState()
164 {
165     if (!isCurlInit_) {
166         NETMGR_LOG_E("Curl global does not initialized, attempting to reinitialize.");
167         isCurlInit_ = NetHttpProbe::CurlGlobalInit();
168     }
169     return isCurlInit_;
170 }
171 
CleanHttpCurl()172 void NetHttpProbe::CleanHttpCurl()
173 {
174     if (httpCurl_) {
175         if (curlMulti_) {
176             curl_multi_remove_handle(curlMulti_, httpCurl_);
177         }
178         curl_easy_cleanup(httpCurl_);
179         httpCurl_ = nullptr;
180     }
181 
182     if (httpsCurl_) {
183         if (curlMulti_) {
184             curl_multi_remove_handle(curlMulti_, httpsCurl_);
185         }
186         curl_easy_cleanup(httpsCurl_);
187         httpsCurl_ = nullptr;
188     }
189 
190     if (httpResolveList_) {
191         curl_slist_free_all(httpResolveList_);
192         httpResolveList_ = nullptr;
193     }
194 
195     if (httpsResolveList_) {
196         curl_slist_free_all(httpsResolveList_);
197         httpsResolveList_ = nullptr;
198     }
199 
200     if (curlMulti_) {
201         curl_multi_cleanup(curlMulti_);
202         curlMulti_ = nullptr;
203     }
204 }
205 
ClearProbeResult()206 void NetHttpProbe::ClearProbeResult()
207 {
208     httpProbeResult_ = {};
209     httpsProbeResult_ = {};
210 }
211 
ExtractDomainFormUrl(const std::string & url)212 std::string NetHttpProbe::ExtractDomainFormUrl(const std::string &url)
213 {
214     if (url.empty()) {
215         return std::string();
216     }
217 
218     size_t doubleSlashPos = url.find("//");
219     if (doubleSlashPos == std::string::npos) {
220         return url;
221     }
222 
223     std::string domain;
224     size_t domainStartPos = doubleSlashPos + 2;
225     size_t domainEndPos = url.find('/', domainStartPos);
226     if (domainEndPos != std::string::npos) {
227         domain = url.substr(domainStartPos, domainEndPos - domainStartPos);
228     } else {
229         domain = url.substr(domainStartPos);
230     }
231     return domain;
232 }
233 
GetAddrInfo(const std::string & domain)234 std::string NetHttpProbe::GetAddrInfo(const std::string &domain)
235 {
236     if (domain.empty()) {
237         NETMGR_LOG_E("domain is empty");
238         return std::string();
239     }
240 
241     std::vector<AddrInfo> result;
242     AddrInfo hints = {};
243     std::string serverName;
244     if (NetsysController::GetInstance().GetAddrInfo(domain, serverName, hints, netId_, result) < 0) {
245         NETMGR_LOG_E("Get net[%{public}d] address info failed,errno[%{public}d]:%{public}s", netId_, errno,
246                      strerror(errno));
247         return std::string();
248     }
249     if (result.empty()) {
250         NETMGR_LOG_E("Get net[%{public}d] address info return nullptr result", netId_);
251         return std::string();
252     }
253 
254     std::string ipAddress;
255     char ip[DOMAIN_IP_ADDR_LEN_MAX] = {0};
256     for (auto &node : result) {
257         errno_t err = memset_s(&ip, sizeof(ip), 0, sizeof(ip));
258         if (err != EOK) {
259             NETMGR_LOG_E("memset_s failed,err:%{public}d", err);
260             return std::string();
261         }
262 
263         if (node.aiFamily == AF_INET) {
264             if (!inet_ntop(AF_INET, &node.aiAddr.sin.sin_addr, ip, sizeof(ip))) {
265                 continue;
266             }
267         } else if (node.aiFamily == AF_INET6) {
268             if (!inet_ntop(AF_INET6, &node.aiAddr.sin6.sin6_addr, ip, sizeof(ip))) {
269                 continue;
270             }
271         }
272         if (ipAddress.find(ip) != std::string::npos) {
273             continue;
274         }
275         ipAddress = ipAddress.empty() ? (ipAddress + ip) : (ipAddress + ADDR_SEPARATOR + ip);
276     }
277     return ipAddress;
278 }
279 
InitHttpCurl(ProbeType probeType)280 bool NetHttpProbe::InitHttpCurl(ProbeType probeType)
281 {
282     curlMulti_ = curl_multi_init();
283     if (curlMulti_ == nullptr) {
284         NETMGR_LOG_E("curl_multi_init() failed.");
285         return false;
286     }
287 
288     if (IsHttpDetect(probeType)) {
289         httpCurl_ = curl_easy_init();
290         if (!httpCurl_) {
291             NETMGR_LOG_E("httpCurl_ init failed");
292             return false;
293         }
294     }
295 
296     if (IsHttpsDetect(probeType)) {
297         httpsCurl_ = curl_easy_init();
298         if (!httpsCurl_) {
299             NETMGR_LOG_E("httpsCurl_ init failed");
300             return false;
301         }
302     }
303     return true;
304 }
305 
SetCurlOptions(ProbeType probeType,const std::string & httpUrl,const std::string & httpsUrl)306 bool NetHttpProbe::SetCurlOptions(ProbeType probeType, const std::string &httpUrl, const std::string &httpsUrl)
307 {
308     bool useProxy = false;
309     if (!SetProxyOption(probeType, useProxy)) {
310         NETMGR_LOG_E("Set curl proxy option failed.");
311         return false;
312     }
313     if (!SendDnsProbe(probeType, httpUrl, httpsUrl, useProxy)) {
314         NETMGR_LOG_E("Set resolve option failed.");
315         return false;
316     }
317 
318     if (IsHttpDetect(probeType)) {
319         if (!SetHttpOptions(ProbeType::PROBE_HTTP, httpCurl_, httpUrl)) {
320             return false;
321         }
322     }
323 
324     if (IsHttpsDetect(probeType)) {
325         if (!SetHttpOptions(ProbeType::PROBE_HTTPS, httpsCurl_, httpsUrl)) {
326             return false;
327         }
328     }
329 
330     return true;
331 }
332 
HeaderCallback(char * buffer,size_t size,size_t nitems,void * userdata)333 size_t NetHttpProbe::HeaderCallback(char* buffer, size_t size, size_t nitems, void* userdata)
334 {
335     std::string* data = static_cast<std::string*>(userdata);
336     NETMGR_LOG_D("recv data size:[%{public}zu] nitems:[%{public}zu]", size, nitems);
337     if (size * nitems > CURL_MAX_HTTP_HEADER) {
338         NETMGR_LOG_E("recv data error, greater than 100K");
339         return 0;
340     }
341     if (data != nullptr && buffer != nullptr) {
342         data->append(buffer, size * nitems);
343     }
344     return size * nitems;
345 }
346 
SetHttpOptions(ProbeType probeType,CURL * curl,const std::string & url)347 bool NetHttpProbe::SetHttpOptions(ProbeType probeType, CURL *curl, const std::string &url)
348 {
349     if (!curl) {
350         NETMGR_LOG_E("curl is nullptr");
351         return false;
352     }
353     if (url.empty()) {
354         NETMGR_LOG_E("Probe url is empty");
355         return false;
356     }
357     struct curl_slist *list = nullptr;
358     list = curl_slist_append(list, DEFAULT_USER_AGENT.c_str());
359     list = curl_slist_append(list, CONNECTION_PROPERTY);
360     list = curl_slist_append(list, ACCEPT_ENCODING);
361     if (!list) {
362         NETMGR_LOG_E("add request header properties failed.");
363         return false;
364     }
365 
366     NETPROBE_CURL_EASY_SET_OPTION(curl, CURLOPT_VERBOSE, 0L);
367     NETPROBE_CURL_EASY_SET_OPTION(curl, CURLOPT_FORBID_REUSE, 1L);
368     NETPROBE_CURL_EASY_SET_OPTION(curl, CURLOPT_URL, url.c_str());
369     if (IsHttpsDetect(probeType)) {
370         /* the connection succeeds regardless of the peer certificate validation */
371         NETPROBE_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 0L);
372         /* the connection succeeds regardless of the names in the certificate. */
373         NETPROBE_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 0L);
374     }
375     NETPROBE_CURL_EASY_SET_OPTION(curl, CURLOPT_NOSIGNAL, 1L);
376     /* connection timeout time */
377     NETPROBE_CURL_EASY_SET_OPTION(curl, CURLOPT_CONNECTTIMEOUT_MS, CURL_CONNECT_TIME_OUT_MS);
378     /* transfer operation timeout time */
379     NETPROBE_CURL_EASY_SET_OPTION(curl, CURLOPT_TIMEOUT_MS, CURL_OPERATE_TIME_OUT_MS);
380     NETPROBE_CURL_EASY_SET_OPTION(curl, CURLOPT_INTERFACE, netLinkInfo_.ifaceName_.c_str());
381     NETPROBE_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERFUNCTION, NetHttpProbe::HeaderCallback);
382     NETPROBE_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERDATA, &respHeader_);
383     NETPROBE_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADER, 1L);
384     NETPROBE_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTPHEADER, list);
385     NETPROBE_CURL_EASY_SET_OPTION(curl, CURLOPT_ERRORBUFFER, errBuffer);
386 
387     CURLMcode code = curl_multi_add_handle(curlMulti_, curl);
388     if (code != CURLM_OK) {
389         NETMGR_LOG_E("curl multi add handle failed, code:[%{public}d]", code);
390         return false;
391     }
392     return true;
393 }
394 
SetProxyOption(ProbeType probeType,bool & useHttpProxy)395 bool NetHttpProbe::SetProxyOption(ProbeType probeType, bool &useHttpProxy)
396 {
397     useHttpProxy = false;
398     /* WIFI or Ethernet require the use of proxy for network detection */
399     if (netBearType_ != BEARER_WIFI && netBearType_ != BEARER_ETHERNET) {
400         NETMGR_LOG_W("Net:[%{public}d] bear type:[%{public}d], no proxy probe required.", netId_, probeType);
401         return true;
402     }
403 
404     std::string proxyHost;
405     int32_t proxyPort = 0;
406     /* Prioritize the use of global HTTP proxy, if there is no global proxy, use network http proxy */
407     if (!LoadProxy(proxyHost, proxyPort)) {
408         NETMGR_LOG_D("global http proxy or network proxy is empty.");
409         return true;
410     }
411 
412     std::string proxyDomain = ExtractDomainFormUrl(proxyHost);
413     if (proxyDomain.empty()) {
414         NETMGR_LOG_E("Extract proxy domain from host return empty.");
415         return true;
416     }
417     std::string proxyIpAddress = GetAddrInfo(proxyDomain);
418 
419     NETMGR_LOG_I("Using proxy for http probe on netId:[%{public}d]", netId_);
420     bool ret = true;
421     if (IsHttpDetect(probeType)) {
422         if (!SetProxyInfo(httpCurl_, proxyHost, proxyPort)) {
423             NETMGR_LOG_E("Set proxy info failed.");
424         }
425         ret &= SetResolveOption(ProbeType::PROBE_HTTP, proxyDomain, proxyIpAddress, proxyPort);
426     }
427 
428     if (IsHttpsDetect(probeType)) {
429         if (!SetProxyInfo(httpsCurl_, proxyHost, proxyPort)) {
430             NETMGR_LOG_E("Set proxy info failed.");
431         }
432         ret &= SetResolveOption(ProbeType::PROBE_HTTPS, proxyDomain, proxyIpAddress, proxyPort);
433     }
434     useHttpProxy = true;
435     return ret;
436 }
437 
SetProxyInfo(CURL * curlHandler,const std::string & proxyHost,int32_t proxyPort)438 bool NetHttpProbe::SetProxyInfo(CURL *curlHandler, const std::string &proxyHost, int32_t proxyPort)
439 {
440     auto proxyType = (proxyHost.find("https://") != std::string::npos) ? CURLPROXY_HTTPS : CURLPROXY_HTTP;
441     if (curlHandler == nullptr) {
442         NETMGR_LOG_E("curlHandler is nullptr.");
443         return false;
444     }
445     NETPROBE_CURL_EASY_SET_OPTION(curlHandler, CURLOPT_PROXY, proxyHost.c_str());
446     NETPROBE_CURL_EASY_SET_OPTION(curlHandler, CURLOPT_PROXYPORT, proxyPort);
447     NETPROBE_CURL_EASY_SET_OPTION(curlHandler, CURLOPT_PROXYTYPE, proxyType);
448     NETPROBE_CURL_EASY_SET_OPTION(curlHandler, CURLOPT_HTTPPROXYTUNNEL, 1L);
449     if (!SetUserInfo(curlHandler)) {
450         NETMGR_LOG_E("Set user info failed.");
451     }
452     return true;
453 }
454 
SetUserInfo(CURL * curlHandler)455 bool NetHttpProbe::SetUserInfo(CURL *curlHandler)
456 {
457     HttpProxy tempProxy;
458     auto userInfoHelp = NetProxyUserinfo::GetInstance();
459     userInfoHelp.GetHttpProxyHostPass(tempProxy);
460     auto username = tempProxy.GetUsername();
461     auto passwd = tempProxy.GetPassword();
462     if (!username.empty()) {
463         NETPROBE_CURL_EASY_SET_OPTION(curlHandler, CURLOPT_PROXYUSERNAME, username.c_str());
464         NETPROBE_CURL_EASY_SET_OPTION(curlHandler, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
465         if (!passwd.empty()) {
466             NETPROBE_CURL_EASY_SET_OPTION(curlHandler, CURLOPT_PROXYPASSWORD, passwd.c_str());
467         }
468     }
469     return true;
470 }
471 
SetResolveOption(ProbeType probeType,const std::string & domain,const std::string & ipAddress,int32_t port)472 bool NetHttpProbe::SetResolveOption(ProbeType probeType, const std::string &domain, const std::string &ipAddress,
473                                     int32_t port)
474 {
475     if (domain.empty()) {
476         NETMGR_LOG_E("domain is empty");
477         return false;
478     }
479 
480     if (ipAddress.empty()) {
481         NETMGR_LOG_E("ipAddress is empty");
482         return false;
483     }
484 
485     std::string resolve = domain + SYMBOL_COLON + std::to_string(port) + SYMBOL_COLON + ipAddress;
486     if (IsHttpDetect(probeType)) {
487         httpResolveList_ = curl_slist_append(httpResolveList_, resolve.c_str());
488         NETPROBE_CURL_EASY_SET_OPTION(httpCurl_, CURLOPT_RESOLVE, httpResolveList_);
489     }
490 
491     if (IsHttpsDetect(probeType)) {
492         httpsResolveList_ = curl_slist_append(httpsResolveList_, resolve.c_str());
493         NETPROBE_CURL_EASY_SET_OPTION(httpsCurl_, CURLOPT_RESOLVE, httpsResolveList_);
494     }
495     return true;
496 }
497 
SendDnsProbe(ProbeType probeType,const std::string & httpUrl,const std::string & httpsUrl,const bool useProxy)498 bool NetHttpProbe::SendDnsProbe(ProbeType probeType, const std::string &httpUrl, const std::string &httpsUrl,
499                                 const bool useProxy)
500 {
501     if (useProxy) {
502         NETMGR_LOG_W("Net[%{public}d] probe use http proxy,no DNS detection required.", netId_);
503         return true;
504     }
505 
506     std::string httpDomain;
507     std::string httpsDomain;
508     if (IsHttpDetect(probeType)) {
509         httpDomain = ExtractDomainFormUrl(httpUrl);
510         if (httpDomain.empty()) {
511             NETMGR_LOG_E("The http domain extracted from [%{public}s] is empty", httpUrl.c_str());
512             return false;
513         }
514     }
515 
516     if (IsHttpsDetect(probeType)) {
517         httpsDomain = ExtractDomainFormUrl(httpsUrl);
518         if (httpsDomain.empty()) {
519             NETMGR_LOG_E("The https domain extracted from [%{public}s] is empty", httpsUrl.c_str());
520             return false;
521         }
522     }
523 
524     std::string ipAddress;
525     if (httpDomain == httpsDomain) {
526         NETMGR_LOG_I("Get net[%{public}d] ip addr for HTTP&HTTPS probe url ", netId_);
527         ipAddress = GetAddrInfo(httpDomain);
528         return SetResolveOption(ProbeType::PROBE_HTTP, httpDomain, ipAddress, DEFAULT_HTTP_PORT) &&
529                SetResolveOption(ProbeType::PROBE_HTTPS, httpsDomain, ipAddress, DEFAULT_HTTPS_PORT);
530     }
531 
532     if (IsHttpDetect(probeType)) {
533         NETMGR_LOG_I("Get net[%{public}d] ip addr for HTTP probe url ", netId_);
534         ipAddress = GetAddrInfo(httpDomain);
535         return SetResolveOption(ProbeType::PROBE_HTTP, httpDomain, ipAddress, DEFAULT_HTTP_PORT);
536     }
537 
538     if (IsHttpsDetect(probeType)) {
539         NETMGR_LOG_I("Get net[%{public}d] ip addr for HTTPS probe url ", netId_);
540         ipAddress = GetAddrInfo(httpsDomain);
541         return SetResolveOption(ProbeType::PROBE_HTTPS, httpsDomain, ipAddress, DEFAULT_HTTPS_PORT);
542     }
543     return false;
544 }
545 
SendHttpProbeRequest()546 void NetHttpProbe::SendHttpProbeRequest()
547 {
548     if (!curlMulti_) {
549         NETMGR_LOG_E("curlMulti_ is nullptr");
550         return;
551     }
552 
553     int running = 0;
554     do {
555         CURLMcode result = curl_multi_perform(curlMulti_, &running);
556         if ((result == CURLM_OK) && running) {
557             result = curl_multi_poll(curlMulti_, nullptr, 0, PERFORM_POLL_INTERVAL_MS, nullptr);
558         }
559         if (result != CURLM_OK) {
560             NETMGR_LOG_E("curl_multi_perform() error, error code:[%{public}d]", result);
561             break;
562         }
563     } while (running);
564 }
565 
GetHeaderField(std::string key)566 std::string NetHttpProbe::GetHeaderField(std::string key)
567 {
568     std::string result = "";
569     if (respHeader_.empty()) {
570         NETMGR_LOG_I("net[%{public}d], probeType[%{public}d] response header empty", netId_, probeType_);
571         return result;
572     }
573     size_t start = respHeader_.find(key);
574     if (start != std::string::npos) {
575         start += key.length();
576         size_t end = respHeader_.find(NEW_LINE_STR, start);
577         result = respHeader_.substr(start, end - start);
578         result = CommonUtils::Trim(result);
579     }
580     NETMGR_LOG_I("net[%{public}d], probeType[%{public}d], key:[%{public}s]", netId_, probeType_, key.c_str());
581     return result;
582 }
583 
CheckRespCode(int64_t respCode)584 int64_t NetHttpProbe::CheckRespCode(int64_t respCode)
585 {
586     NETMGR_LOG_D("net[%{public}d], response code before check:%{public}" PRId64, netId_, respCode);
587     if (respCode == HTTP_OK_CODE) {
588         std::string contentLengthValue = GetHeaderField(CONTENT_LENGTH_KEY);
589         int32_t lengthValue = contentLengthValue.empty() ? DEFAULT_CONTENT_LENGTH_VALUE :
590             CommonUtils::StrToInt(contentLengthValue, DEFAULT_CONTENT_LENGTH_VALUE);
591         if (lengthValue == DEFAULT_CONTENT_LENGTH_VALUE) {
592             if (respHeader_.empty()) {
593                 NETMGR_LOG_I("net[%{public}d], response code 200 with content length -1, consider as fail", netId_);
594                 return FAIL_CODE;
595             }
596         } else if (lengthValue < MIN_VALID_CONTENT_LENGTH_VALUE) {
597             NETMGR_LOG_I("net[%{public}d], response code 200, content length less 5, consider as fail", netId_);
598             return FAIL_CODE;
599         }
600 
601         std::string value = GetHeaderField(CONNECTION_KEY);
602         value = CommonUtils::ToLower(value);
603         if (CONNECTION_CLOSE_VALUE.compare(value) == 0 &&
604             probeType_ == ProbeType::PROBE_HTTP) {
605             NETMGR_LOG_I("net[%{public}d] http detection, response code 200 with connection close, consider as fail",
606                 netId_);
607             return FAIL_CODE;
608         }
609     }
610     int64_t result = respCode;
611     if (IsHttpDetect(probeType_)) {
612         result = CheckClientErrorRespCode(result);
613     }
614     return result;
615 }
616 
CheckClientErrorRespCode(int64_t respCode)617 int64_t NetHttpProbe::CheckClientErrorRespCode(int64_t respCode)
618 {
619     int64_t result = respCode;
620     if (respCode >= HTTP_RES_CODE_BAD_REQUEST && respCode <= HTTP_RES_CODE_CLIENT_ERRORS_MAX) {
621         std::string errMsg(errBuffer);
622         if ((errMsg.find(HTML_TITLE_HTTP_EN) != std::string::npos ||
623             errMsg.find(HTML_TITLE_HTTPS_EN) != std::string::npos) &&
624             errMsg.find(KEY_WORDS_REDIRECTION) != std::string::npos) {
625             NETMGR_LOG_I("net[%{public}d] reset url in content, consider as portal when http return %{public}" PRId64,
626                 netId_, respCode);
627             result = PORTAL_CODE;
628         }
629     }
630     return result;
631 }
632 
RecvHttpProbeResponse()633 void NetHttpProbe::RecvHttpProbeResponse()
634 {
635     if (!curlMulti_) {
636         NETMGR_LOG_E("curlMulti_ is nullptr");
637         return;
638     }
639     CURLMsg *curlMsg = nullptr;
640     int32_t msgQueue = 0;
641     while ((curlMsg = curl_multi_info_read(curlMulti_, &msgQueue)) != nullptr) {
642         if (curlMsg->msg != CURLMSG_DONE) {
643             NETMGR_LOG_W("curl multi read not done, msg:[%{public}d]", curlMsg->msg);
644             continue;
645         }
646 
647         if (!curlMsg->easy_handle) {
648             NETMGR_LOG_E("Read nullptr curl easy handle");
649             continue;
650         }
651 
652         int64_t responseCode = 0;
653         curl_easy_getinfo(curlMsg->easy_handle, CURLINFO_RESPONSE_CODE, &responseCode);
654         responseCode = CheckRespCode(responseCode);
655         std::string redirectUrl;
656         char* url = nullptr;
657         curl_easy_getinfo(curlMsg->easy_handle, CURLINFO_REDIRECT_URL, &url);
658         if (url != nullptr) {
659             redirectUrl = url;
660         } else {
661             curl_easy_getinfo(curlMsg->easy_handle, CURLINFO_EFFECTIVE_URL, &url);
662             redirectUrl = url;
663         }
664 
665         if (curlMsg->easy_handle == httpCurl_) {
666             httpProbeResult_ = {responseCode, redirectUrl};
667             NETMGR_LOG_I("Recv net[%{public}d] http probe response, code:[%{public}d]", netId_,
668                          httpProbeResult_.GetCode());
669         } else if (curlMsg->easy_handle == httpsCurl_) {
670             httpsProbeResult_ = {responseCode, redirectUrl};
671             NETMGR_LOG_I("Recv net[%{public}d] https probe response, code:[%{public}d]", netId_,
672                          httpsProbeResult_.GetCode());
673         } else {
674             NETMGR_LOG_E("Unknown curl handle.");
675         }
676     }
677 }
678 
LoadProxy(std::string & proxyHost,int32_t & proxyPort)679 int32_t NetHttpProbe::LoadProxy(std::string &proxyHost, int32_t &proxyPort)
680 {
681     std::lock_guard<std::mutex> locker(proxyMtx_);
682     if (!globalHttpProxy_.GetHost().empty() && defaultUseGlobalHttpProxy_) {
683         proxyHost = globalHttpProxy_.GetHost();
684         proxyPort = static_cast<int32_t>(globalHttpProxy_.GetPort());
685     } else if (!netLinkInfo_.httpProxy_.GetHost().empty()) {
686         proxyHost = netLinkInfo_.httpProxy_.GetHost();
687         proxyPort = static_cast<int32_t>(netLinkInfo_.httpProxy_.GetPort());
688     } else {
689         return false;
690     }
691     return true;
692 }
693 
ProbeWithoutGlobalHttpProxy()694 void NetHttpProbe::ProbeWithoutGlobalHttpProxy()
695 {
696     defaultUseGlobalHttpProxy_ = false;
697 }
698 
IsHttpDetect(ProbeType probeType)699 bool NetHttpProbe::IsHttpDetect(ProbeType probeType)
700 {
701     return probeType == ProbeType::PROBE_HTTP || probeType == ProbeType::PROBE_HTTP_FALLBACK;
702 }
703 
IsHttpsDetect(ProbeType probeType)704 bool NetHttpProbe::IsHttpsDetect(ProbeType probeType)
705 {
706     return probeType == ProbeType::PROBE_HTTPS || probeType == ProbeType::PROBE_HTTPS_FALLBACK;
707 }
708 
709 } // namespace NetManagerStandard
710 } // namespace OHOS
711