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