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