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
34 #include "curl/curl.h"
35 #include "netstack_log.h"
36
37 constexpr int32_t INET_OPTION_SUC = 1;
38 constexpr size_t MAX_DISPLAY_NUM = 2;
39
40 namespace OHOS::NetStack::CommonUtils {
41 const std::regex IP_PATTERN{
42 "((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)"};
43 const std::regex IP_MASK_PATTERN{
44 "((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)/"
45 "(3[0-2]|[1-2]\\d|\\d)"};
46 const std::regex IPV6_PATTERN{"([\\da-fA-F]{0,4}:){2,7}([\\da-fA-F]{0,4})"};
47 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])"};
48 std::mutex g_commonUtilsMutex;
49
Split(const std::string & str,const std::string & sep)50 std::vector<std::string> Split(const std::string &str, const std::string &sep)
51 {
52 std::string s = str;
53 std::vector<std::string> res;
54 while (!s.empty()) {
55 auto pos = s.find(sep);
56 if (pos == std::string::npos) {
57 res.emplace_back(s);
58 break;
59 }
60 res.emplace_back(s.substr(0, pos));
61 s = s.substr(pos + sep.size());
62 }
63 return res;
64 }
65
Split(const std::string & str,const std::string & sep,size_t size)66 std::vector<std::string> Split(const std::string &str, const std::string &sep, size_t size)
67 {
68 std::string s = str;
69 std::vector<std::string> res;
70 while (!s.empty()) {
71 if (res.size() + 1 == size) {
72 res.emplace_back(s);
73 break;
74 }
75
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
Strip(const std::string & str,char ch)87 std::string Strip(const std::string &str, char ch)
88 {
89 int64_t i = 0;
90 while (static_cast<size_t>(i) < str.size() && str[i] == ch) {
91 ++i;
92 }
93 int64_t j = static_cast<int64_t>(str.size()) - 1;
94 while (j > 0 && str[j] == ch) {
95 --j;
96 }
97 if (i >= 0 && static_cast<size_t>(i) < str.size() && j >= 0 && static_cast<size_t>(j) < str.size() &&
98 j - i + 1 > 0) {
99 return str.substr(i, j - i + 1);
100 }
101 return "";
102 }
103
ToLower(const std::string & s)104 std::string ToLower(const std::string &s)
105 {
106 std::string res = s;
107 std::transform(res.begin(), res.end(), res.begin(), tolower);
108 return res;
109 }
110
ToString(const std::list<std::string> & lists,char tab)111 std::string ToString(const std::list<std::string> &lists, char tab)
112 {
113 std::string str;
114 for (auto it = lists.begin(); it != lists.end(); ++it) {
115 if (it != lists.begin()) {
116 str.append(1, tab);
117 }
118 str.append(*it);
119 }
120 return str;
121 }
122
HasInternetPermission()123 bool HasInternetPermission()
124 {
125 #ifndef OH_CORE_NETSTACK_PERMISSION_CHECK
126 #ifdef FUZZ_TEST
127 return true;
128 #endif
129 int testSock = socket(AF_INET, SOCK_STREAM, 0);
130 if (testSock < 0 && errno == EPERM) {
131 NETSTACK_LOGE("make tcp testSock failed errno is %{public}d %{public}s", errno, strerror(errno));
132 return false;
133 }
134 if (testSock > 0) {
135 close(testSock);
136 }
137 return true;
138 #else
139 constexpr int inetGroup = 40002003; // 3003 in gateway shell.
140 int groupNum = getgroups(0, nullptr);
141 if (groupNum <= 0) {
142 NETSTACK_LOGE("no group of INTERNET permission");
143 return false;
144 }
145 auto groups = (gid_t *)malloc(groupNum * sizeof(gid_t));
146 groupNum = getgroups(groupNum, groups);
147 for (int i = 0; i < groupNum; i++) {
148 if (groups[i] == inetGroup) {
149 free(groups);
150 return true;
151 }
152 }
153 free(groups);
154 NETSTACK_LOGE("INTERNET permission denied by group");
155 return false;
156 #endif
157 }
158
EndsWith(const std::string & str,const std::string & suffix)159 bool EndsWith(const std::string &str, const std::string &suffix)
160 {
161 if (str.length() < suffix.length()) {
162 return false;
163 }
164 return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
165 }
166
Trim(std::string str)167 std::string Trim(std::string str)
168 {
169 size_t start = str.find_first_not_of(" \t\n\r");
170 size_t end = str.find_last_not_of(" \t\n\r");
171 if (start == std::string::npos || end == std::string::npos) {
172 return "";
173 } else {
174 return str.substr(start, end - start + 1);
175 }
176 }
177
IsMatch(const std::string & str,const std::string & patternStr)178 bool IsMatch(const std::string &str, const std::string &patternStr)
179 {
180 if (patternStr.empty()) {
181 return false;
182 }
183 if (patternStr == "*") {
184 return true;
185 }
186 if (!IsRegexValid(patternStr)) {
187 NETSTACK_LOGD("Invalid pattern string: %{public}s", patternStr.c_str());
188 return patternStr == str;
189 }
190 std::regex pattern(ReplaceCharacters(patternStr));
191 bool isMacth = patternStr != "" && std::regex_match(str, pattern);
192 if (isMacth) {
193 NETSTACK_LOGD("Match patternStr: %{public}s", patternStr.c_str());
194 }
195 return isMacth;
196 }
197
InsertCharBefore(const std::string & input,const char from,const char preChar,const char nextChar)198 std::string InsertCharBefore(const std::string &input, const char from, const char preChar, const char nextChar)
199 {
200 std::string output = input;
201 char arr[] = {preChar, from};
202 unsigned long strSize = sizeof(arr) / sizeof(arr[0]);
203 std::string str(arr, strSize);
204 std::size_t pos = output.find(from);
205 std::size_t length = output.length();
206 while (pos <= length - 1 && pos != std::string::npos) {
207 char nextCharTemp = pos == length - 1 ? '\0' : output[pos + 1];
208 if (nextChar == '\0' || nextCharTemp == '\0' || nextCharTemp != nextChar) {
209 output.replace(pos, 1, str);
210 length += (strSize - 1);
211 }
212 pos = output.find(from, pos + strSize);
213 }
214 return output;
215 }
216
ReplaceCharacters(const std::string & input)217 std::string ReplaceCharacters(const std::string &input)
218 {
219 std::string output = InsertCharBefore(input, '*', '.', '\0');
220 output = InsertCharBefore(output, '.', '\\', '*');
221 return output;
222 }
223
IsRegexValid(const std::string & regex)224 bool IsRegexValid(const std::string ®ex)
225 {
226 if (Trim(regex).empty()) {
227 return false;
228 }
229 return regex_match(regex, std::regex("^[a-zA-Z0-9\\-_\\.*]+$"));
230 }
231
GetHostnameFromURL(const std::string & url)232 std::string GetHostnameFromURL(const std::string &url)
233 {
234 if (url.empty()) {
235 return "";
236 }
237 std::string delimiter = "://";
238 std::string tempUrl = url;
239 std::replace(tempUrl.begin(), tempUrl.end(), '\\', '/');
240 size_t posStart = tempUrl.find(delimiter);
241 if (posStart != std::string::npos) {
242 posStart += delimiter.length();
243 } else {
244 posStart = 0;
245 }
246 size_t notSlash = tempUrl.find_first_not_of('/', posStart);
247 if (notSlash != std::string::npos) {
248 posStart = notSlash;
249 }
250 size_t posEnd = std::min({ tempUrl.find(':', posStart),
251 tempUrl.find('/', posStart), tempUrl.find('?', posStart) });
252 if (posEnd != std::string::npos) {
253 return tempUrl.substr(posStart, posEnd - posStart);
254 }
255 return tempUrl.substr(posStart);
256 }
257
IsExcluded(const std::string & str,const std::string & exclusions,const std::string & split)258 bool IsExcluded(const std::string &str, const std::string &exclusions, const std::string &split)
259 {
260 if (Trim(exclusions).empty()) {
261 return false;
262 }
263 std::size_t start = 0;
264 std::size_t end = exclusions.find(split);
265 while (end != std::string::npos) {
266 if (end - start > 0 && IsMatch(str, Trim(exclusions.substr(start, end - start)))) {
267 return true;
268 }
269 start = end + 1;
270 end = exclusions.find(split, start);
271 }
272 return IsMatch(str, Trim(exclusions.substr(start)));
273 }
274
IsHostNameExcluded(const std::string & url,const std::string & exclusions,const std::string & split)275 bool IsHostNameExcluded(const std::string &url, const std::string &exclusions, const std::string &split)
276 {
277 std::string hostName = GetHostnameFromURL(url);
278 NETSTACK_LOGD("hostName is: %{public}s", hostName.c_str());
279 return IsExcluded(hostName, exclusions, split);
280 }
281
IsValidIPV4(const std::string & ip)282 bool IsValidIPV4(const std::string &ip)
283 {
284 return IsValidIP(ip, AF_INET);
285 }
286
IsValidIPV6(const std::string & ip)287 bool IsValidIPV6(const std::string &ip)
288 {
289 return IsValidIP(ip, AF_INET6);
290 }
291
IsValidIP(const std::string & ip,int af)292 bool IsValidIP(const std::string& ip, int af)
293 {
294 if (ip.empty()) {
295 return false;
296 }
297 #ifdef WINDOWS_PLATFORM
298 if (af == AF_INET6) {
299 struct sockaddr_in6 sa;
300 return inet_pton(af, ip.c_str(), &(sa.sin6_addr)) == INET_OPTION_SUC;
301 } else {
302 struct sockaddr_in sa;
303 return inet_pton(af, ip.c_str(), &(sa.sin_addr)) == INET_OPTION_SUC;
304 }
305 #else
306 if (af == AF_INET6) {
307 struct in6_addr addr;
308 return inet_pton(af, ip.c_str(), reinterpret_cast<void *>(&addr)) == INET_OPTION_SUC;
309 } else {
310 struct in_addr addr;
311 return inet_pton(af, ip.c_str(), reinterpret_cast<void *>(&addr)) == INET_OPTION_SUC;
312 }
313 #endif
314 }
315
MaskIpv4(std::string & maskedResult)316 std::string MaskIpv4(std::string &maskedResult)
317 {
318 int maxDisplayNum = MAX_DISPLAY_NUM;
319 for (char &i : maskedResult) {
320 if (i == '/') {
321 break;
322 }
323 if (maxDisplayNum > 0) {
324 if (i == '.') {
325 maxDisplayNum--;
326 }
327 } else {
328 if (i != '.') {
329 i = '*';
330 }
331 }
332 }
333 return maskedResult;
334 }
335
MaskIpv6(std::string & maskedResult)336 std::string MaskIpv6(std::string &maskedResult)
337 {
338 size_t colonCount = 0;
339 for (char &i : maskedResult) {
340 if (i == '/') {
341 break;
342 }
343 if (i == ':') {
344 colonCount++;
345 }
346
347 if (colonCount >= MAX_DISPLAY_NUM) {
348 if (i != ':') {
349 i = '*';
350 }
351 }
352 }
353 return maskedResult;
354 }
355
AnonymizeIp(std::string & input)356 std::string AnonymizeIp(std::string &input)
357 {
358 if (input.empty()) {
359 return input;
360 }
361 std::lock_guard<std::mutex> lock(g_commonUtilsMutex);
362 std::string maskedResult{input};
363 if (std::regex_match(maskedResult, IP_PATTERN) || std::regex_match(maskedResult, IP_MASK_PATTERN)) {
364 return MaskIpv4(maskedResult);
365 }
366 if (std::regex_match(maskedResult, IPV6_PATTERN) || std::regex_match(maskedResult, IPV6_MASK_PATTERN)) {
367 return MaskIpv6(maskedResult);
368 }
369 return input;
370 }
371 } // namespace OHOS::NetStack::CommonUtils