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