• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2025 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 "netstack_common_utils.h"
17 
18 #ifdef WINDOWS_PLATFORM
19 #include <winsock2.h>
20 #include <ws2tcpip.h>
21 #pragma comment(lib, "ws2_32.lib")
22 #else
23 #include <arpa/inet.h>
24 #endif
25 
26 #include <algorithm>
27 #include <cctype>
28 #include <cerrno>
29 #include <regex>
30 #include <string>
31 #include <unistd.h>
32 #include <vector>
33 #include <fstream>
34 #include <sstream>
35 #if !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
36 #include <filesystem>
37 #endif
38 
39 #if HAS_NETMANAGER_BASE
40 #include <openssl/evp.h>
41 #endif // HAS_NETMANAGER_BASE
42 
43 #include "netstack_log.h"
44 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) && !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
45 #include "netstack_apipolicy_utils.h"
46 #include "netstack_bundle_utils.h"
47 #endif
48 #if HAS_NETMANAGER_BASE
49 #include "net_conn_client.h"
50 #include "network_security_config.h"
51 #endif // HAS_NETMANAGER_BASE
52 
53 constexpr int32_t INET_OPTION_SUC = 1;
54 constexpr size_t MAX_DISPLAY_NUM = 2;
55 #if HAS_NETMANAGER_BASE
56 constexpr unsigned int SHA256_LEN = 32;
57 #endif
58 constexpr int SHA256_BASE64_LEN = 44;  // 32-byte base64 -> 44 bytes
59 constexpr int PINNED_PREFIX_LEN = 8; // strlen("sha256//")
60 
61 namespace OHOS::NetStack::CommonUtils {
62 const std::regex IP_PATTERN{
63     "((2([0-4]\\d|5[0-5])|1\\d\\d|[1-9]\\d|\\d)\\.){3}(2([0-4]\\d|5[0-5])|1\\d\\d|[1-9]\\d|\\d)"};
64 const std::regex IP_MASK_PATTERN{
65     "((2([0-4]\\d|5[0-5])|1\\d\\d|[1-9]\\d|\\d)\\.){3}(2([0-4]\\d|5[0-5])|1\\d\\d|[1-9]\\d|\\d)/"
66     "(3[0-2]|[1-2]\\d|\\d)"};
67 const std::regex IPV6_PATTERN{"([\\da-fA-F]{0,4}:){2,7}([\\da-fA-F]{0,4})"};
68 const std::regex IPV6_MASK_PATTERN{"([\\da-fA-F]{0,4}:){2,7}([\\da-fA-F]{0,4})/(1[0-2][0-8]|[1-9]\\d|[1-9])"};
69 static const std::string PROTOCOL_WSS = "wss";
70 std::mutex g_commonUtilsMutex;
71 
Split(const std::string & str,const std::string & sep)72 std::vector<std::string> Split(const std::string &str, const std::string &sep)
73 {
74     std::string s = str;
75     std::vector<std::string> res;
76     while (!s.empty()) {
77         auto pos = s.find(sep);
78         if (pos == std::string::npos) {
79             res.emplace_back(s);
80             break;
81         }
82         res.emplace_back(s.substr(0, pos));
83         s = s.substr(pos + sep.size());
84     }
85     return res;
86 }
87 
Split(const std::string & str,const std::string & sep,size_t size)88 std::vector<std::string> Split(const std::string &str, const std::string &sep, size_t size)
89 {
90     std::string s = str;
91     std::vector<std::string> res;
92     while (!s.empty()) {
93         if (res.size() + 1 == size) {
94             res.emplace_back(s);
95             break;
96         }
97 
98         auto pos = s.find(sep);
99         if (pos == std::string::npos) {
100             res.emplace_back(s);
101             break;
102         }
103         res.emplace_back(s.substr(0, pos));
104         s = s.substr(pos + sep.size());
105     }
106     return res;
107 }
108 
Strip(const std::string & str,char ch)109 std::string Strip(const std::string &str, char ch)
110 {
111     int64_t i = 0;
112     while (static_cast<size_t>(i) < str.size() && str[i] == ch) {
113         ++i;
114     }
115     int64_t j = static_cast<int64_t>(str.size()) - 1;
116     while (j > 0 && str[j] == ch) {
117         --j;
118     }
119     if (i >= 0 && static_cast<size_t>(i) < str.size() && j >= 0 && static_cast<size_t>(j) < str.size() &&
120         j - i + 1 > 0) {
121         return str.substr(i, j - i + 1);
122     }
123     return "";
124 }
125 
ToLower(const std::string & s)126 std::string ToLower(const std::string &s)
127 {
128     std::string res = s;
129     std::transform(res.begin(), res.end(), res.begin(), tolower);
130     return res;
131 }
132 
ToString(const std::list<std::string> & lists,char tab)133 std::string ToString(const std::list<std::string> &lists, char tab)
134 {
135     std::string str;
136     for (auto it = lists.begin(); it != lists.end(); ++it) {
137         if (it != lists.begin()) {
138             str.append(1, tab);
139         }
140         str.append(*it);
141     }
142     return str;
143 }
144 
HasInternetPermission()145 bool HasInternetPermission()
146 {
147 #ifndef OH_CORE_NETSTACK_PERMISSION_CHECK
148 #ifdef FUZZ_TEST
149     return true;
150 #endif
151 #ifdef DT_TEST
152     return true;
153 #endif
154     int testSock = socket(AF_INET, SOCK_STREAM, 0);
155     if (testSock < 0 && errno == EPERM) {
156         NETSTACK_LOGE("make tcp testSock failed errno is %{public}d %{public}s", errno, strerror(errno));
157         return false;
158     }
159     if (testSock > 0) {
160         close(testSock);
161     }
162     return true;
163 #else
164     constexpr int inetGroup = 40002003; // 3003 in gateway shell.
165     int groupNum = getgroups(0, nullptr);
166     if (groupNum <= 0) {
167         NETSTACK_LOGE("no group of INTERNET permission");
168         return false;
169     }
170     auto groups = (gid_t *)malloc(groupNum * sizeof(gid_t));
171     if (groups == nullptr) {
172         NETSTACK_LOGE("INTERNET permission denied by malloc");
173         return false;
174     }
175     groupNum = getgroups(groupNum, groups);
176     for (int i = 0; i < groupNum; i++) {
177         if (groups[i] == inetGroup) {
178             free(groups);
179             return true;
180         }
181     }
182     free(groups);
183     NETSTACK_LOGE("INTERNET permission denied by group");
184     return false;
185 #endif
186 }
187 
IsAtomicService(std::string & bundleName)188 bool IsAtomicService(std::string &bundleName)
189 {
190 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) && !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
191     return BundleUtils::IsAtomicService(bundleName);
192 #else
193     return false;
194 #endif
195 }
196 
IsAllowedHostname(const std::string & bundleName,const std::string & domainType,const std::string & url)197 bool IsAllowedHostname(const std::string &bundleName, const std::string &domainType, const std::string &url)
198 {
199 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) && !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
200     if (bundleName.empty()) {
201         NETSTACK_LOGE("isAllowedHostnameForAtomicService bundleName is empty");
202         return true;
203     }
204     auto hostname = GetHostnameWithProtocolAndPortFromURL(url);
205     if (hostname.empty()) {
206         NETSTACK_LOGE("isAllowedHostnameForAtomicService url hostname is empty");
207         return true;
208     }
209     return ApiPolicyUtils::IsAllowedHostname(bundleName, domainType, hostname);
210 #else
211     return true;
212 #endif
213 }
214 
EndsWith(const std::string & str,const std::string & suffix)215 bool EndsWith(const std::string &str, const std::string &suffix)
216 {
217     if (str.length() < suffix.length()) {
218         return false;
219     }
220     return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
221 }
222 
Trim(std::string str)223 std::string Trim(std::string str)
224 {
225     size_t start = str.find_first_not_of(" \t\n\r");
226     size_t end = str.find_last_not_of(" \t\n\r");
227     if (start == std::string::npos || end == std::string::npos) {
228         return "";
229     } else {
230         return str.substr(start, end - start + 1);
231     }
232 }
233 
IsMatch(const std::string & str,const std::string & patternStr)234 bool IsMatch(const std::string &str, const std::string &patternStr)
235 {
236     if (patternStr.empty()) {
237         return false;
238     }
239     if (patternStr == "*") {
240         return true;
241     }
242     if (!IsRegexValid(patternStr)) {
243         NETSTACK_LOGD("Invalid pattern");
244         return patternStr == str;
245     }
246     std::regex pattern(ReplaceCharacters(patternStr));
247     bool isMacth = patternStr != "" && std::regex_match(str, pattern);
248     if (isMacth) {
249         NETSTACK_LOGD("Match patternStr");
250     }
251     return isMacth;
252 }
253 
InsertCharBefore(const std::string & input,const char from,const char preChar,const char nextChar)254 std::string InsertCharBefore(const std::string &input, const char from, const char preChar, const char nextChar)
255 {
256     std::string output = input;
257     char arr[] = {preChar, from};
258     unsigned long strSize = sizeof(arr) / sizeof(arr[0]);
259     std::string str(arr, strSize);
260     std::size_t pos = output.find(from);
261     std::size_t length = output.length();
262     while (pos <= length - 1 && pos != std::string::npos) {
263         char nextCharTemp = pos == length - 1 ? '\0' : output[pos + 1];
264         if (nextChar == '\0' || nextCharTemp == '\0' || nextCharTemp != nextChar) {
265             output.replace(pos, 1, str);
266             length += (strSize - 1);
267         }
268         pos = output.find(from, pos + strSize);
269     }
270     return output;
271 }
272 
ReplaceCharacters(const std::string & input)273 std::string ReplaceCharacters(const std::string &input)
274 {
275     std::string output = InsertCharBefore(input, '*', '.', '\0');
276     output = InsertCharBefore(output, '.', '\\', '*');
277     return output;
278 }
279 
IsRegexValid(const std::string & regex)280 bool IsRegexValid(const std::string &regex)
281 {
282     if (Trim(regex).empty()) {
283         return false;
284     }
285     return regex_match(regex, std::regex("^[a-zA-Z0-9\\-_\\.*]+$"));
286 }
287 
GetProtocolFromURL(const std::string & url)288 std::string GetProtocolFromURL(const std::string &url)
289 {
290     std::string delimiter = "://";
291     size_t pos = url.find(delimiter);
292     if (pos != std::string::npos) {
293         return url.substr(0, pos);
294     }
295     return "";
296 }
297 
GetPortFromURL(const std::string & url)298 std::string GetPortFromURL(const std::string &url)
299 {
300     std::string delimiter = "://";
301     std::string protocol = GetProtocolFromURL(url);
302     std::string hostname = GetHostnameFromURL(url);
303     size_t start = protocol.empty() ? hostname.size() : protocol.size() + delimiter.size() + hostname.size();
304     size_t posStart = url.find_first_of(':', start);
305     if (posStart == std::string::npos) {
306         return "";
307     }
308     size_t posEnd = std::min({url.find('/', start), url.find('?', start)});
309     if (posEnd == std::string::npos) {
310         return url.substr(posStart + 1);
311     }
312     if (posStart > posEnd) {
313         return "";
314     }
315     return url.substr(posStart + 1, posEnd - posStart - 1);
316 }
317 
GetHostnameFromURL(const std::string & url)318 std::string GetHostnameFromURL(const std::string &url)
319 {
320     if (url.empty()) {
321         return "";
322     }
323     std::string delimiter = "://";
324     std::string tempUrl = url;
325     std::replace(tempUrl.begin(), tempUrl.end(), '\\', '/');
326     size_t posStart = tempUrl.find(delimiter);
327     if (posStart != std::string::npos) {
328         posStart += delimiter.length();
329     } else {
330         posStart = 0;
331     }
332     size_t notSlash = tempUrl.find_first_not_of('/', posStart);
333     if (notSlash != std::string::npos) {
334         posStart = notSlash;
335     }
336     size_t posEnd = std::min({ tempUrl.find(':', posStart),
337                               tempUrl.find('/', posStart), tempUrl.find('?', posStart) });
338     if (posEnd != std::string::npos) {
339         return tempUrl.substr(posStart, posEnd - posStart);
340     }
341     return tempUrl.substr(posStart);
342 }
343 
GetHostnameWithProtocolAndPortFromURL(const std::string & url)344 std::string GetHostnameWithProtocolAndPortFromURL(const std::string& url)
345 {
346     std::string delimiter = "://";
347     std::string portDelimiter = ":";
348     auto hostname = GetHostnameFromURL(url);
349     if (!hostname.empty()) {
350         std::string protocol = GetProtocolFromURL(url);
351         if (!protocol.empty()) {
352             hostname = protocol + delimiter + hostname;
353         }
354         if (protocol != PROTOCOL_WSS) {
355             std::string port = GetPortFromURL(url);
356             if (!port.empty()) {
357                 hostname += portDelimiter + port;
358             }
359         }
360     }
361     return hostname;
362 }
363 
IsExcluded(const std::string & str,const std::string & exclusions,const std::string & split)364 bool IsExcluded(const std::string &str, const std::string &exclusions, const std::string &split)
365 {
366     if (Trim(exclusions).empty()) {
367         return false;
368     }
369     std::size_t start = 0;
370     std::size_t end = exclusions.find(split);
371     while (end != std::string::npos) {
372         if (end - start > 0 && IsMatch(str, Trim(exclusions.substr(start, end - start)))) {
373             return true;
374         }
375         start = end + 1;
376         end = exclusions.find(split, start);
377     }
378     return IsMatch(str, Trim(exclusions.substr(start)));
379 }
380 
IsHostNameExcluded(const std::string & url,const std::string & exclusions,const std::string & split)381 bool IsHostNameExcluded(const std::string &url, const std::string &exclusions, const std::string &split)
382 {
383     std::string hostName = GetHostnameFromURL(url);
384     return IsExcluded(hostName, exclusions, split);
385 }
386 
DetectIPType(const std::string & ip)387 int DetectIPType(const std::string &ip)
388 {
389     if (ip.empty()) {
390         return INVALID_IP_TYPE;
391     }
392     if (IsValidIPV4(ip)) {
393         return AF_INET;
394     }
395     if (IsValidIPV6(ip)) {
396         return AF_INET6;
397     }
398     return INVALID_IP_TYPE;
399 }
400 
IsValidIPV4(const std::string & ip)401 bool IsValidIPV4(const std::string &ip)
402 {
403     return IsValidIP(ip, AF_INET);
404 }
405 
IsValidIPV6(const std::string & ip)406 bool IsValidIPV6(const std::string &ip)
407 {
408     return IsValidIP(ip, AF_INET6);
409 }
410 
IsValidIP(const std::string & ip,int af)411 bool IsValidIP(const std::string& ip, int af)
412 {
413     if (ip.empty()) {
414         return false;
415     }
416 #ifdef WINDOWS_PLATFORM
417     if (af == AF_INET6) {
418         struct sockaddr_in6 sa;
419         return inet_pton(af, ip.c_str(), &(sa.sin6_addr)) == INET_OPTION_SUC;
420     } else {
421         struct sockaddr_in sa;
422         return inet_pton(af, ip.c_str(), &(sa.sin_addr)) == INET_OPTION_SUC;
423     }
424 #else
425     if (af == AF_INET6) {
426         struct in6_addr addr;
427         return inet_pton(af, ip.c_str(), reinterpret_cast<void *>(&addr)) == INET_OPTION_SUC;
428     } else {
429         struct in_addr addr;
430         return inet_pton(af, ip.c_str(), reinterpret_cast<void *>(&addr)) == INET_OPTION_SUC;
431     }
432 #endif
433 }
434 
MaskIpv4(std::string & maskedResult)435 std::string MaskIpv4(std::string &maskedResult)
436 {
437     int maxDisplayNum = MAX_DISPLAY_NUM;
438     for (char &i : maskedResult) {
439         if (i == '/') {
440             break;
441         }
442         if (maxDisplayNum > 0) {
443             if (i == '.') {
444                 maxDisplayNum--;
445             }
446         } else {
447             if (i != '.') {
448                 i = '*';
449             }
450         }
451     }
452     return maskedResult;
453 }
454 
MaskIpv6(std::string & maskedResult)455 std::string MaskIpv6(std::string &maskedResult)
456 {
457     size_t colonCount = 0;
458     for (char &i : maskedResult) {
459         if (i == '/') {
460             break;
461         }
462         if (i == ':') {
463             colonCount++;
464         }
465 
466         if (colonCount >= MAX_DISPLAY_NUM) {
467             if (i != ':') {
468                 i = '*';
469             }
470         }
471     }
472     return maskedResult;
473 }
474 
AnonymizeIp(std::string & input)475 std::string AnonymizeIp(std::string &input)
476 {
477     if (input.empty()) {
478         return input;
479     }
480     std::lock_guard<std::mutex> lock(g_commonUtilsMutex);
481     std::string maskedResult{input};
482     if (std::regex_match(maskedResult, IP_PATTERN) || std::regex_match(maskedResult, IP_MASK_PATTERN)) {
483         return MaskIpv4(maskedResult);
484     }
485     if (std::regex_match(maskedResult, IPV6_PATTERN) || std::regex_match(maskedResult, IPV6_MASK_PATTERN)) {
486         return MaskIpv6(maskedResult);
487     }
488     return input;
489 }
490 
GetBundleName()491 std::optional<std::string> GetBundleName()
492 {
493 #if HAS_NETMANAGER_BASE
494     return OHOS::NetManagerStandard::NetConnClient::ObtainBundleNameForSelf();
495 #endif
496     return std::nullopt;
497 }
498 
GetFileDataFromFilePath(const std::string & filePath,std::string & fileData)499 bool GetFileDataFromFilePath(const std::string& filePath, std::string& fileData)
500 {
501 #if !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
502     std::error_code error;
503     auto path = std::filesystem::absolute(filePath, error);
504     if (error) {
505         NETSTACK_LOGE("Failed to obtain the absolute path: %{public}s", error.message().c_str());
506         return false;
507     }
508     std::ifstream file(path);
509 #else
510     std::ifstream file(filePath);
511 #endif
512     if (file.is_open()) {
513         std::stringstream buffer;
514         buffer << file.rdbuf();
515         file.close();
516         fileData = buffer.str();
517         return true;
518     } else {
519         NETSTACK_LOGE("Failed to obtain the file data stream.");
520         return false;
521     }
522 }
523 
Sha256sum(unsigned char * buf,size_t buflen,std::string & digestStr)524 bool Sha256sum(unsigned char *buf, size_t buflen, std::string &digestStr)
525 {
526 #ifdef HAS_NETMANAGER_BASE
527     EVP_MD_CTX *mdctx = EVP_MD_CTX_create();
528     unsigned int digestLen = 0;
529     unsigned char digest[SHA256_LEN];
530     unsigned char out[SHA256_BASE64_LEN + 1] = {0};
531     if (!mdctx) {
532         NETSTACK_LOGE("create MD_CTX failed.");
533         return false;
534     }
535     if (!EVP_DigestInit(mdctx, EVP_sha256())) {
536         NETSTACK_LOGE("EVP_DigestInit failed.");
537         return false;
538     }
539     if (!EVP_DigestUpdate(mdctx, buf, buflen)) {
540         NETSTACK_LOGE("EVP_DigestUpdate failed.");
541         return false;
542     }
543     if (!EVP_DigestFinal_ex(mdctx, digest, &digestLen)) {
544         NETSTACK_LOGE("EVP_DigestFinal_ex failed.");
545         return false;
546     }
547     EVP_MD_CTX_free(mdctx);
548     if (digestLen != SHA256_LEN) {
549         NETSTACK_LOGE("SHA256 length invalid");
550         return false;
551     }
552     int base64Len = EVP_EncodeBlock(out, digest, SHA256_LEN);
553     if (base64Len != SHA256_BASE64_LEN) {
554         NETSTACK_LOGE("SHA256-Base64 length invalid.");
555         return false;
556     }
557     digestStr = std::string(reinterpret_cast<const char *>(out), SHA256_BASE64_LEN);
558     return true;
559 #else
560     return false;
561 #endif
562 }
563 
IsCertPubKeyInPinned(const std::string & certPubKeyDigest,const std::string & pinnedPubkey)564 bool IsCertPubKeyInPinned(const std::string &certPubKeyDigest, const std::string &pinnedPubkey)
565 {
566     auto begin = pinnedPubkey.find("sha256//");
567     if (begin != 0) {
568         NETSTACK_LOGE("pinnedPubkey format invalid, should start with sha256//");
569         return false;
570     }
571     while (begin < pinnedPubkey.size()) {
572         auto end = pinnedPubkey.find(";", begin);
573         if (end == std::string::npos) {
574             end = pinnedPubkey.size();
575         }
576         if (pinnedPubkey.find("sha256//", begin) != begin) {
577             NETSTACK_LOGE("pinnedPubkey format invalid, should be like sha256//[hash1];sha256//[hash2]");
578             begin = end + 1;
579             continue;
580         }
581         if (end - begin != PINNED_PREFIX_LEN + SHA256_BASE64_LEN) {
582             NETSTACK_LOGE("pinnedPubkey format invalid, hash length not match");
583             begin = end + 1;
584             continue;
585         }
586         std::string candidate = pinnedPubkey.substr(begin + PINNED_PREFIX_LEN, SHA256_BASE64_LEN);
587         if (candidate == certPubKeyDigest) {
588             return true;
589         }
590         begin = end + 1;
591     }
592     return false;
593 }
594 
IsCleartextPermitted(const std::string & url,const std::string & protocol)595 bool IsCleartextPermitted(const std::string &url, const std::string &protocol)
596 {
597     bool isCleartextPermitted = true;
598 #if HAS_NETMANAGER_BASE
599     using namespace OHOS::NetManagerStandard;
600     bool isComponetCfg = true;
601     int32_t ret = NetworkSecurityConfig::GetInstance().IsCleartextCfgByComponent("Network Kit", isComponetCfg);
602     if (ret || !isComponetCfg) {
603         NETSTACK_LOGD("Network Kit Component Not Cfg or Cfg False");
604         return isCleartextPermitted;
605     }
606     if (url.find(protocol) != std::string::npos) {
607         std::string hostName = GetHostnameFromURL(url);
608         NetworkSecurityConfig::GetInstance().IsCleartextPermitted(hostName, isCleartextPermitted);
609     }
610 #endif
611     return isCleartextPermitted;
612 }
613 
IsValidPort(const uint32_t & port)614 bool IsValidPort(const uint32_t &port)
615 {
616     if (port < 0 || port > MAX_PORT) {
617         return false;
618     }
619     return true;
620 }
621 
ToAnonymousIp(const std::string & input)622 std::string ToAnonymousIp(const std::string &input)
623 {
624     std::string maskedResult = input;
625     // Mask ipv4 address.
626     if (std::regex_match(maskedResult, IP_PATTERN) || std::regex_match(maskedResult, IP_MASK_PATTERN)) {
627         MaskIpv4(maskedResult);
628         return maskedResult;
629     }
630     // Mask ipv6 address.
631     if (std::regex_match(maskedResult, IPV6_PATTERN) || std::regex_match(maskedResult, IPV6_MASK_PATTERN)) {
632         MaskIpv6(maskedResult);
633         return maskedResult;
634     }
635     return input;
636 }
637 } // namespace OHOS::NetStack::CommonUtils