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