• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }