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