1 /*
2 * Copyright (c) 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
16 #include "thread"
17 #include <arpa/inet.h>
18 #include <cstring>
19 #include <iostream>
20 #include <netinet/in.h>
21 #include <string>
22 #include <sys/socket.h>
23 #include <unistd.h>
24
25 static std::string g_defaultFileContent =
26 "// PAC (Proxy Auto-Configuration) 脚本示例\n"
27 "// 包含所有主要PAC函数的使用示例,只演示基础函数使用。\n"
28 "// 目前扩展辅助函数功能支持不全面,不建议使用。\n"
29 "// isInNetEx和isInNet功能相同\n"
30 "// myIpAddressEx返回所有本地ip\n"
31 "// dnsResolveEx和dnsResolve相同,只返回一个地址\n"
32 "// sortIpAddressList不支持\n"
33 "\n"
34 "function isIP(str) {\n"
35 " // 正则表达式检查是否为有效的 IP 地址\n"
36 " var ipPattern = "
37 "/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|["
38 "01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;\n"
39 " return ipPattern.test(str);\n"
40 "}\n"
41 "\n"
42 "function FindProxyForURL(url, host) {\n"
43 " // console.info(1);\n"
44 " // 本机地址直接访问\n"
45 " if (host === \"127.0.0.1\" || host === \"localhost\") {\n"
46 " return \"DIRECT\";\n"
47 " }\n"
48 " // 2. 检查本地域名\n"
49 " //本机域名直接访问\n"
50 " if (dnsDomainIs(host, \".local\") ||\n"
51 " dnsDomainIs(host, \".localhost\") ||\n"
52 " localHostOrDomainIs(host, \"localhost\")) {\n"
53 " return \"DIRECT\";\n"
54 " }\n"
55 " // 3. 检查内网地址\n"
56 " // 内网地址直接访问\n"
57 " var myIP = myIpAddress();\n"
58 " if (isInNet(host, \"192.168.0.0\", \"255.255.0.0\") ||\n"
59 " isInNet(host, \"10.0.0.0\", \"255.0.0.0\") ||\n"
60 " isInNet(host, \"172.16.0.0\", \"255.240.0.0\") ||\n"
61 " isInNet(myIP, \"192.168.0.0\", \"255.255.0.0\")) {\n"
62 " return \"DIRECT\";\n"
63 " }\n"
64 " // 5 ip地址使用代理\n"
65 " if (isIP(host)) {\n"
66 " return \"PROXY special-proxy.com:8000\";\n"
67 " }\n"
68 " var pattern=/baidu/g; \n"
69 " if(pattern.test(host)){\n"
70 " return \"PROXY special-proxy.com:7000\";\n"
71 " }\n"
72 " // 4. 检查纯主机使用代理\n"
73 " if (isPlainHostName(host)) {\n"
74 " return \"PROXY special-proxy.com:8001\";\n"
75 " }\n"
76 " // 4. 基于时间的规则\n"
77 " // 周一到周五的时间\n"
78 " if (weekdayRange(\"MON\", \"FRI\")) {\n"
79 " //九点到18点的时间\n"
80 " if (timeRange(9, 18)) {\n"
81 " if (dnsDomainIs(host, \".company.com\")) {\n"
82 " return \"PROXY special-proxy.com:8002\";\n"
83 " }\n"
84 " } else {\n"
85 " return \"PROXY special-proxy.com:8102\";\n"
86 " }\n"
87 " }\n"
88 " // 5. 基于日期的规则\n"
89 " // 1月到3月\n"
90 " if (dateRange(\"JAN\", \"MAR\")) {\n"
91 " // 域名判断\n"
92 " if (shExpMatch(host, \"*.special.com\")) {\n"
93 " return \"PROXY special-proxy.com:8003\";\n"
94 " }else{\n"
95 " return \"PROXY special-proxy.com:8103\";\n"
96 " }\n"
97 " }\n"
98 " // 6. 检查域名级别\n"
99 " if (dnsDomainLevels(host) >= 3) {\n"
100 " // 多级域名使用特定代理\n"
101 " return \"PROXY special-proxy.com:8004\";\n"
102 " }\n"
103 " // 7. 可解析性检查\n"
104 " if (isResolvable(host)) {\n"
105 " var resolvedIP = dnsResolve(host);\n"
106 " if (resolvedIP && isInNet(resolvedIP, \"116.205.4.0\", \"255.255.255.0\")) {\n"
107 " return \"PROXY special-proxy.com:8005\";\n"
108 " }\n"
109 " }\n"
110 " // 8. Shell表达式匹配\n"
111 " if (shExpMatch(url, \"http://download*.example.com/*\") ||\n"
112 " shExpMatch(url, \"https://*.cdn.com/*\")) {\n"
113 " return \"PROXY special-proxy.com:8006\";\n"
114 " }\n"
115 " // 扩展函数示例(部分浏览器支持)\n"
116 " try {\n"
117 " // 9. 扩展IP检查 返回所有的本地ip,myIpAddress只会返回127.0.0.1,\n"
118 " var allMyIPs = myIpAddressEx();\n"
119 " // isInNetEx和isInNet功能相同\n"
120 " if (allMyIPs && isInNetEx(host, allMyIPs)) {\n"
121 " return \"PROXY special-proxy.com:8007\";\n"
122 " }\n"
123 " // 10. 扩展DNS解析\n"
124 " //dnsResolveEx和dnsResolve相同,不支持返回多个域名的ip\n"
125 " var allIPs = dnsResolveEx(host);\n"
126 " if (allIPs) {\n"
127 " //sortIpAddressList函数不支持\n"
128 " var sortedIPs = sortIpAddressList(allIPs);\n"
129 " // 使用排序后的IP列表进行进一步处理\n"
130 " }\n"
131 " // 11. 扩展可解析性检查\n"
132 " //isResolvableEx和isResolvable相同\n"
133 " if (isResolvableEx(host)) {\n"
134 " // 进行额外的处理\n"
135 " }\n"
136 " } catch (e) {\n"
137 " // 扩展函数不支持时的降级处理\n"
138 " }\n"
139 " // 默认规则\n"
140 " return \"PROXY default-proxy.com:8080; DIRECT\";\n"
141 "}";
142
IsTestRequest(const std::string & request)143 bool IsTestRequest(const std::string &request)
144 {
145 std::size_t pos = request.find("GET ");
146 if (pos != std::string::npos) {
147 std::string pathPart = request.substr(pos + 4);
148 std::size_t httpPos = pathPart.find("http://");
149 if (httpPos != std::string::npos) {
150 std::size_t slashPos = pathPart.find("/", httpPos + 7);
151 if (slashPos != std::string::npos) {
152 pathPart = pathPart.substr(slashPos);
153 }
154 }
155 return (pathPart.find("/test ") == 0 || pathPart.find("/test/ ") == 0 || pathPart.find("/test\r") == 0 ||
156 pathPart.find("/test/\r") == 0 || pathPart.find("/test\n") == 0 || pathPart.find("/test/\n") == 0);
157 }
158 return false;
159 }
160
GetHeaderValue(const std::string & request,const std::string & headerName)161 std::string GetHeaderValue(const std::string &request, const std::string &headerName)
162 {
163 std::string headerPrefix = headerName + ": ";
164 size_t pos = request.find(headerPrefix);
165 if (pos == std::string::npos) {
166 return "";
167 }
168 size_t valueStart = pos + headerPrefix.length();
169 size_t valueEnd = request.find("\r\n", valueStart);
170 if (valueEnd == std::string::npos) {
171 return request.substr(valueStart);
172 }
173 return request.substr(valueStart, valueEnd - valueStart);
174 }
175
InitializeServerSocket(int32_t port,const std::string & ip)176 int32_t InitializeServerSocket(int32_t port, const std::string &ip)
177 {
178 int32_t serverFd = socket(AF_INET, SOCK_STREAM, 0);
179 if (serverFd < 0) {
180 return -1;
181 }
182 int32_t opt = 1;
183 if (setsockopt(serverFd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
184 return -1;
185 }
186 struct sockaddr_in address;
187 address.sin_family = AF_INET;
188 if (ip.empty()) {
189 address.sin_addr.s_addr = INADDR_ANY;
190 } else {
191 inet_pton(AF_INET, ip.c_str(), &(address.sin_addr));
192 }
193 address.sin_port = htons(port);
194 if (bind(serverFd, reinterpret_cast<const sockaddr *>(&address), sizeof(address)) < 0) {
195 return -1;
196 }
197 #define TIME_OUT_3S 3
198 if (listen(serverFd, TIME_OUT_3S) < 0) {
199 return -1;
200 }
201 return serverFd;
202 }
203
HandleTestRequest(int32_t socket,const std::string & request,struct sockaddr_in & address)204 void HandleTestRequest(int32_t socket, const std::string &request, struct sockaddr_in &address)
205 {
206 std::string clientIp = inet_ntoa(address.sin_addr);
207 int32_t clientPort = ntohs(address.sin_port);
208 bool isProxy = request.find("Proxy-Connection") != std::string::npos;
209 std::string info = "{\"ClientIp\":\"";
210 info += clientIp;
211 info += "\",";
212 info += "\"ClientPort\":\"";
213 info += std::to_string(clientPort);
214 info += "\",";
215 info += "\"Proxy-Connection\":\"";
216 info += GetHeaderValue(request, "Proxy-Connection");
217 info += "\",";
218 info += "\"GlobalProxyIp\":\"";
219 info += GetHeaderValue(request, "GlobalProxyIp");
220 info += "\",";
221 info += "\"GlobalProxyPort\":\"";
222 info += GetHeaderValue(request, "GlobalProxyPort");
223 info += "\",";
224 info += "\"Proxy-Port\":\"";
225 info += GetHeaderValue(request, "Proxy-Port");
226 info += "\",";
227 info += "\"isProxy\":\"";
228 info += std::to_string(isProxy);
229 info += "\"}";
230 printf("\033[34mproxy server read client data %s \n\033[0m", info.c_str());
231 std::string response =
232 "HTTP/1.1 200 OK\r\n"
233 "Content-Type: text/json; charset=UTF-8\r\n"
234 "Content-Length: " +
235 std::to_string(info.length()) +
236 "\r\n"
237 "Connection: close\r\n"
238 "\r\n" +
239 info;
240 send(socket, response.c_str(), response.length(), 0);
241 }
242
HandlePacRequest(int32_t socket,const std::string & content)243 void HandlePacRequest(int32_t socket, const std::string &content)
244 {
245 std::string filename = "download.txt";
246 printf("\033[34msend pac script %.128s \n\033[0m", content.c_str());
247
248 std::string response =
249 "HTTP/1.1 200 OK\r\n"
250 "Content-Type: text/plain; charset=UTF-8\r\n"
251 "Content-Disposition: attachment; filename=\"" +
252 filename +
253 "\"\r\n"
254 "Content-Length: " +
255 std::to_string(content.length()) +
256 "\r\n"
257 "Connection: close\r\n"
258 "\r\n" +
259 content;
260
261 send(socket, response.c_str(), response.length(), 0);
262 }
263
HandleClientConnection(int32_t serverFd,std::string pacScript)264 void HandleClientConnection(int32_t serverFd, std::string pacScript)
265 {
266 struct sockaddr_in address;
267 int32_t addrlen = sizeof(address);
268 if (pacScript.empty()) {
269 pacScript = g_defaultFileContent;
270 }
271 #define SIZE_1024 1024
272 char buffer[SIZE_1024] = {0};
273
274 int32_t clientSocket =
275 accept(serverFd, reinterpret_cast<sockaddr *>(&address), reinterpret_cast<socklen_t *>(&addrlen));
276 if (clientSocket < 0) {
277 return;
278 }
279
280 read(clientSocket, buffer, SIZE_1024);
281 std::string request(buffer);
282
283 if (IsTestRequest(request)) {
284 HandleTestRequest(clientSocket, request, address);
285 } else {
286 HandlePacRequest(clientSocket, pacScript);
287 }
288
289 close(clientSocket);
290 }
291
292 static bool g_isRunning = true;
293
StartHttpServer(int32_t port,std::string ip,std::string pacScript)294 void StartHttpServer(int32_t port, std::string ip, std::string pacScript)
295 {
296 std::thread httpThread([port, ip, pacScript]() {
297 int32_t serverFd = InitializeServerSocket(port, ip);
298 if (serverFd < 0) {
299 return 1;
300 }
301 std::string displayIp = ip.empty() ? "127.0.0.1" : ip;
302 std::cout << "Pac Server Start ,PacFileURL http://" << displayIp << ":" << port << "/" << std::endl;
303 while (g_isRunning) {
304 HandleClientConnection(serverFd, pacScript);
305 }
306 close(serverFd);
307 return 0;
308 });
309
310 httpThread.detach();
311 }