1 /*
2 * Copyright (c) 2025-2025 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 #include <unistd.h>
16 #include <arpa/inet.h>
17 #include <netinet/tcp.h>
18 #include <netinet/in.h>
19 #include "netstack_chr_client.h"
20 #include "netstack_common_utils.h"
21 #include "netstack_log.h"
22 #include "i_netstack_chr_client.h"
23
24 namespace OHOS::NetStack::ChrClient {
25
26 static constexpr const long HTTP_REQUEST_SUCCESS = 200;
27 static constexpr const int HTTP_FILE_TRANSFER_SIZE_THRESHOLD = 100000;
28 static constexpr const int HTTP_FILE_TRANSFER_TIME_THRESHOLD = 500000;
29
GetInstance()30 NetStackChrClient &NetStackChrClient::GetInstance()
31 {
32 static NetStackChrClient instance;
33 return instance;
34 }
35
GetAddrFromSock(int sockfd,std::string & srcIp,std::string & dstIp,uint16_t & srcPort,uint16_t & dstPort)36 int NetStackChrClient::GetAddrFromSock(
37 int sockfd, std::string &srcIp, std::string &dstIp, uint16_t &srcPort, uint16_t &dstPort)
38 {
39 sockaddr_storage localss{};
40 sockaddr_storage peerss{};
41 socklen_t addrLen = 0;
42
43 // Get local addr
44 addrLen = sizeof(localss);
45 (void)getsockname(sockfd, reinterpret_cast<sockaddr *>(&localss), &addrLen);
46
47 // Get peer addr
48 addrLen = sizeof(peerss);
49 (void)getpeername(sockfd, reinterpret_cast<sockaddr *>(&peerss), &addrLen);
50
51 char buf[INET6_ADDRSTRLEN] = {0};
52 if (localss.ss_family == AF_INET && peerss.ss_family == AF_INET) {
53 auto *l4 = reinterpret_cast<sockaddr_in *>(&localss);
54 auto *p4 = reinterpret_cast<sockaddr_in *>(&peerss);
55 if (inet_ntop(AF_INET, &l4->sin_addr, buf, sizeof(buf)) != nullptr) {
56 srcIp = buf;
57 srcPort = ntohs(l4->sin_port);
58 }
59 if (inet_ntop(AF_INET, &p4->sin_addr, buf, sizeof(buf)) != nullptr) {
60 dstIp = buf;
61 dstPort = ntohs(p4->sin_port);
62 }
63 } else if (localss.ss_family == AF_INET6 && peerss.ss_family == AF_INET6) {
64 auto *l6 = reinterpret_cast<sockaddr_in6 *>(&localss);
65 auto *p6 = reinterpret_cast<sockaddr_in6 *>(&peerss);
66 if (inet_ntop(AF_INET6, &l6->sin6_addr, buf, sizeof(buf)) != nullptr) {
67 srcIp = buf;
68 srcPort = ntohs(l6->sin6_port);
69 }
70 if (inet_ntop(AF_INET6, &p6->sin6_addr, buf, sizeof(buf)) != nullptr) {
71 dstIp = buf;
72 dstPort = ntohs(p6->sin6_port);
73 }
74 } else {
75 return -1;
76 }
77
78 return 0;
79 }
80
GetTcpInfoFromSock(const curl_socket_t sockfd,DataTransTcpInfo & httpTcpInfo)81 int NetStackChrClient::GetTcpInfoFromSock(const curl_socket_t sockfd, DataTransTcpInfo &httpTcpInfo)
82 {
83 if (sockfd <= 0) {
84 return -1;
85 }
86 struct tcp_info tcpInfo = {};
87 socklen_t infoLen = sizeof(tcpInfo);
88
89 if (getsockopt(sockfd, IPPROTO_TCP, TCP_INFO, &tcpInfo, &infoLen) < 0) {
90 return -1;
91 }
92
93 httpTcpInfo.unacked = tcpInfo.tcpi_unacked;
94 httpTcpInfo.lastDataSent = tcpInfo.tcpi_last_data_sent;
95 httpTcpInfo.lastAckSent = tcpInfo.tcpi_last_ack_sent;
96 httpTcpInfo.lastDataRecv = tcpInfo.tcpi_last_data_recv;
97 httpTcpInfo.lastAckRecv = tcpInfo.tcpi_last_ack_recv;
98 httpTcpInfo.rtt = tcpInfo.tcpi_rtt;
99 httpTcpInfo.rttvar = tcpInfo.tcpi_rttvar;
100 httpTcpInfo.totalRetrans = tcpInfo.tcpi_total_retrans;
101 httpTcpInfo.retransmits = tcpInfo.tcpi_retransmits;
102
103 if (GetAddrFromSock(sockfd, httpTcpInfo.srcIp, httpTcpInfo.dstIp, httpTcpInfo.srcPort, httpTcpInfo.dstPort) == 0) {
104 httpTcpInfo.srcIp = CommonUtils::AnonymizeIp(httpTcpInfo.srcIp);
105 httpTcpInfo.dstIp = CommonUtils::AnonymizeIp(httpTcpInfo.dstIp);
106 }
107
108 return 0;
109 }
110
111 template <typename DataType>
GetNumericAttributeFromCurl(CURL * handle,CURLINFO info)112 DataType NetStackChrClient::GetNumericAttributeFromCurl(CURL *handle, CURLINFO info)
113 {
114 DataType number = 0;
115 CURLcode res = curl_easy_getinfo(handle, info, &number);
116 if (res != CURLE_OK) {
117 return -1;
118 }
119 return number;
120 }
121
GetStringAttributeFromCurl(CURL * handle,CURLINFO info)122 std::string NetStackChrClient::GetStringAttributeFromCurl(CURL *handle, CURLINFO info)
123 {
124 char *result = nullptr;
125 CURLcode res = curl_easy_getinfo(handle, info, &result);
126 if (res != CURLE_OK || result == nullptr) {
127 return std::string();
128 }
129 return std::string(result);
130 }
131
GetRequestStartTime(curl_off_t totalTime)132 long NetStackChrClient::GetRequestStartTime(curl_off_t totalTime)
133 {
134 auto now = std::chrono::system_clock::now();
135 long msCount = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
136 return msCount;
137 }
138
GetHttpInfoFromCurl(CURL * handle,DataTransHttpInfo & httpInfo)139 void NetStackChrClient::GetHttpInfoFromCurl(CURL *handle, DataTransHttpInfo &httpInfo)
140 {
141 (void)curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &httpInfo.responseCode);
142 httpInfo.nameLookUpTime = GetNumericAttributeFromCurl<curl_off_t>(handle, CURLINFO_NAMELOOKUP_TIME_T);
143 httpInfo.connectTime = GetNumericAttributeFromCurl<curl_off_t>(handle, CURLINFO_CONNECT_TIME_T);
144 httpInfo.preTransferTime = GetNumericAttributeFromCurl<curl_off_t>(handle, CURLINFO_PRETRANSFER_TIME_T);
145 httpInfo.startTransferTime = GetNumericAttributeFromCurl<curl_off_t>(handle, CURLINFO_STARTTRANSFER_TIME_T);
146 httpInfo.totalTime = GetNumericAttributeFromCurl<curl_off_t>(handle, CURLINFO_TOTAL_TIME_T);
147 httpInfo.redirectTime = GetNumericAttributeFromCurl<curl_off_t>(handle, CURLINFO_REDIRECT_TIME_T);
148 httpInfo.appconnectTime = GetNumericAttributeFromCurl<curl_off_t>(handle, CURLINFO_APPCONNECT_TIME_T);
149 httpInfo.queueTime = GetNumericAttributeFromCurl<curl_off_t>(handle, CURLINFO_QUEUE_TIME_T);
150 httpInfo.retryAfter = GetNumericAttributeFromCurl<curl_off_t>(handle, CURLINFO_RETRY_AFTER);
151 httpInfo.requestStartTime = GetRequestStartTime(httpInfo.totalTime);
152
153 httpInfo.sizeUpload = GetNumericAttributeFromCurl<curl_off_t>(handle, CURLINFO_SIZE_UPLOAD_T);
154 httpInfo.sizeDownload = GetNumericAttributeFromCurl<curl_off_t>(handle, CURLINFO_SIZE_DOWNLOAD_T);
155 httpInfo.speedDownload = GetNumericAttributeFromCurl<curl_off_t>(handle, CURLINFO_SPEED_DOWNLOAD_T);
156 httpInfo.speedUpload = GetNumericAttributeFromCurl<curl_off_t>(handle, CURLINFO_SPEED_UPLOAD_T);
157
158 httpInfo.redirectCount = GetNumericAttributeFromCurl<long>(handle, CURLINFO_REDIRECT_COUNT);
159 httpInfo.osError = GetNumericAttributeFromCurl<long>(handle, CURLINFO_OS_ERRNO);
160 httpInfo.sslVerifyResult = GetNumericAttributeFromCurl<long>(handle, CURLINFO_PROXY_SSL_VERIFYRESULT);
161 httpInfo.proxyError = GetNumericAttributeFromCurl<long>(handle, CURLINFO_PROXY_ERROR);
162
163 httpInfo.effectiveMethod = GetStringAttributeFromCurl(handle, CURLINFO_EFFECTIVE_METHOD);
164 httpInfo.contentType = GetStringAttributeFromCurl(handle, CURLINFO_CONTENT_TYPE);
165 }
166
ShouldReportHttpAbnormalEvent(const DataTransHttpInfo & httpInfo)167 int NetStackChrClient::ShouldReportHttpAbnormalEvent(const DataTransHttpInfo &httpInfo)
168 {
169 if (httpInfo.curlCode != 0 || httpInfo.responseCode != HTTP_REQUEST_SUCCESS ||
170 httpInfo.osError != 0 || httpInfo.proxyError != 0) {
171 return 0;
172 }
173 if ((httpInfo.sizeUpload + httpInfo.sizeDownload <= HTTP_FILE_TRANSFER_SIZE_THRESHOLD) &&
174 httpInfo.totalTime > HTTP_FILE_TRANSFER_TIME_THRESHOLD) {
175 return 0;
176 }
177
178 return -1;
179 }
180
GetDfxInfoFromCurlHandleAndReport(CURL * handle,int32_t curlCode)181 void NetStackChrClient::GetDfxInfoFromCurlHandleAndReport(CURL *handle, int32_t curlCode)
182 {
183 if (handle == NULL) {
184 return;
185 }
186
187 DataTransChrStats dataTransChrStats{};
188 dataTransChrStats.httpInfo.uid = static_cast<int>(getuid());
189 dataTransChrStats.httpInfo.curlCode = curlCode;
190 if (CommonUtils::GetBundleName().has_value()) {
191 dataTransChrStats.processName = CommonUtils::GetBundleName().value();
192 }
193
194 GetHttpInfoFromCurl(handle, dataTransChrStats.httpInfo);
195 if (ShouldReportHttpAbnormalEvent(dataTransChrStats.httpInfo) != 0) {
196 return;
197 }
198
199 curl_off_t sockfd = 0;
200 curl_easy_getinfo(handle, CURLINFO_ACTIVESOCKET, &sockfd);
201
202 if (GetTcpInfoFromSock(sockfd, dataTransChrStats.tcpInfo) != 0) {
203 NETSTACK_LOGD("Chr client get tcp info from socket failed, sockfd: %{public}" PRId64, sockfd);
204 }
205
206 int ret = netstackChrReport_.ReportCommonEvent(dataTransChrStats);
207 if (ret > 0) {
208 NETSTACK_LOGI("Send to CHR failed, error code %{public}d", ret);
209 }
210 }
211
212 } // namespace OHOS::NetStack::ChrClient