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 ®ex)
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