• 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 #ifdef NETMANAGER_ENABLE_PAC_PROXY
16 #include <cstring>
17 #include <string>
18 #include <curl/curl.h>
19 #include <jerryscript.h>
20 
21 #include "net_pac_manager.h"
22 #include "pac_functions.h"
23 #include "netmanager_base_log.h"
24 
25 namespace OHOS {
26 namespace NetManagerStandard {
27 namespace {
28 constexpr char NULL_CHAR = '\0';
29 constexpr const char FIND_FUNC[] = "FindProxyForURL";
30 constexpr const char USER_AGENT[] = "libcurl-agent/1.0";
31 const std::string TRUE = "true";
32 const std::string FALSE = "false";
33 const std::string EMPTY = "";
34 constexpr int32_t HTTP_CODE_200 = 200;
35 } // namespace
36 
37 static std::string g_script;
38 
NetPACManager()39 NetPACManager::NetPACManager() : pacScriptVal_(jerry_create_undefined()), status_(false), engineInitialized_(false)
40 {
41 }
42 
~NetPACManager()43 NetPACManager::~NetPACManager()
44 {
45     std::lock_guard<std::mutex> guard{pacMutex_};
46     if (jerry_value_is_undefined(pacScriptVal_) == false) {
47         jerry_release_value(pacScriptVal_);
48         pacScriptVal_ = jerry_create_undefined();
49     }
50     if (engineInitialized_) {
51         jerry_cleanup();
52         engineInitialized_ = false;
53     }
54 }
55 
InitPACScript(const std::string & script)56 bool NetPACManager::InitPACScript(const std::string &script)
57 {
58     std::lock_guard<std::mutex> guard{pacMutex_};
59     if (jerry_value_is_undefined(pacScriptVal_) == false) {
60         jerry_release_value(pacScriptVal_);
61         pacScriptVal_ = jerry_create_undefined();
62     }
63     if (engineInitialized_) {
64         jerry_cleanup();
65         engineInitialized_ = false;
66     }
67     const char *pac_script = script.c_str();
68     jerry_init(JERRY_INIT_EMPTY);
69     engineInitialized_ = true;
70     PacFunctions::RegisterPacFunctions();
71     pacScriptVal_ = jerry_parse(NULL, 0, (jerry_char_t *)pac_script, strlen(pac_script), JERRY_PARSE_NO_OPTS);
72     if (jerry_value_is_error(pacScriptVal_)) {
73         jerry_value_t error_value = jerry_get_value_from_error(pacScriptVal_, false);
74         jerry_release_value(pacScriptVal_);
75         jerry_release_value(error_value);
76         pacScriptVal_ = jerry_create_undefined();
77         status_ = false;
78         return false;
79     }
80     status_ = true;
81     return true;
82 }
83 
InitPACScriptWithURL(const std::string & scriptUrl)84 bool NetPACManager::InitPACScriptWithURL(const std::string &scriptUrl)
85 {
86     scriptFileUrl_ = scriptUrl;
87     std::lock_guard<std::mutex> guard{pacMutex_};
88     if (jerry_value_is_undefined(pacScriptVal_) == false) {
89         jerry_release_value(pacScriptVal_);
90         pacScriptVal_ = jerry_create_undefined();
91     }
92     if (engineInitialized_) {
93         jerry_cleanup();
94         engineInitialized_ = false;
95     }
96     g_script.clear();
97     DownloadPACScript(scriptUrl);
98     if (g_script.empty()) {
99         status_ = false;
100         return false;
101     }
102     const char *pac_script = g_script.c_str();
103     jerry_init(JERRY_INIT_EMPTY);
104     engineInitialized_ = true;
105     PacFunctions::RegisterPacFunctions();
106     pacScriptVal_ = jerry_parse(NULL, 0, (jerry_char_t *)pac_script, strlen(pac_script), JERRY_PARSE_NO_OPTS);
107     if (jerry_value_is_error(pacScriptVal_)) {
108         jerry_value_t error_value = jerry_get_value_from_error(pacScriptVal_, false);
109         jerry_release_value(pacScriptVal_);
110         jerry_release_value(error_value);
111         pacScriptVal_ = jerry_create_undefined();
112         status_ = false;
113         return false;
114     }
115     status_ = true;
116     return true;
117 }
118 
FindProxyForURL(const std::string & url,std::string & proxy)119 PAC_STATUS NetPACManager::FindProxyForURL(const std::string &url, std::string &proxy)
120 {
121     return FindProxyForURL(url, ParseHost(url), proxy);
122 }
123 
ReleaseValues(const std::vector<jerry_value_t> & values)124 static void ReleaseValues(const std::vector<jerry_value_t> &values)
125 {
126     for (auto value : values) {
127         jerry_release_value(value);
128     }
129 }
130 
FindProxyForURL(const std::string & url,const std::string & hostStr,std::string & proxy)131 PAC_STATUS NetPACManager::FindProxyForURL(const std::string &url, const std::string &hostStr, std::string &proxy)
132 {
133     if (!status_ && !InitPACScriptWithURL(scriptFileUrl_)) {
134         return PAC_SCRIPT_DOWNLOAD_ERROR;
135     }
136     std::lock_guard<std::mutex> guard{pacMutex_};
137     if (!engineInitialized_ || jerry_value_is_undefined(pacScriptVal_)) {
138         return PAC_SCRIPT_FUNCTION_ERROR;
139     }
140     std::string host = hostStr.empty() ? ParseHost(url) : hostStr;
141     jerry_value_t result = jerry_run(pacScriptVal_);
142     if (jerry_value_is_error(result)) {
143         jerry_value_t error_value = jerry_get_value_from_error(result, false);
144         jerry_release_value(result);
145         jerry_release_value(error_value);
146         return PAC_SCRIPT_RUN_ERROR;
147     }
148     jerry_value_t global_object = jerry_get_global_object();
149     jerry_value_t func_name = jerry_create_string(reinterpret_cast<const jerry_char_t *>(FIND_FUNC));
150     jerry_value_t func = jerry_get_property(global_object, func_name);
151     if (!jerry_value_is_function(func)) {
152         ReleaseValues({func, func_name, global_object, result});
153         return PAC_SCRIPT_FUNCTION_ERROR;
154     }
155     jerry_value_t args[2] = {jerry_create_string(reinterpret_cast<const jerry_char_t *>(url.c_str())),
156                              jerry_create_string(reinterpret_cast<const jerry_char_t *>(host.c_str()))};
157     jerry_value_t call_result = jerry_call_function(func, global_object, args, 2);
158     PAC_STATUS status = PAC_OK;
159     if (!jerry_value_is_error(call_result)) {
160         if (jerry_value_is_string(call_result)) {
161             jerry_length_t strLength = jerry_get_string_length(call_result);
162             if (strLength > 0) {
163                 char buffer[strLength + 1];
164                 jerry_string_to_char_buffer(call_result, reinterpret_cast<jerry_char_t *>(buffer), strLength);
165                 buffer[strLength] = NULL_CHAR;
166                 proxy.append(buffer, strLength);
167             }
168         } else if (jerry_value_is_boolean(call_result)) {
169             proxy.append(jerry_get_boolean_value(call_result) ? TRUE : FALSE);
170         } else if (jerry_value_is_number(call_result)) {
171             proxy.append(std::to_string(static_cast<int>(jerry_get_number_value(call_result))));
172         }
173     } else {
174         jerry_value_t error_value = jerry_get_value_from_error(call_result, false);
175         jerry_release_value(error_value);
176         status = PAC_SCRIPT_CALL_ERROR;
177     }
178     ReleaseValues({call_result, args[0], args[1], func, func_name, global_object, result});
179     return status;
180 }
181 
WriteToString(void * contents,size_t size,size_t nmemb,void * userp)182 static size_t WriteToString(void *contents, size_t size, size_t nmemb, void *userp)
183 {
184     size_t realsize = size * nmemb;
185     std::string *mem = static_cast<std::string *>(userp);
186     mem->append(static_cast<char *>(contents), realsize);
187     g_script.append(static_cast<char *>(contents), realsize);
188     return realsize;
189 }
190 
DownloadPACScript(const std::string & url)191 void NetPACManager::DownloadPACScript(const std::string &url)
192 {
193     struct CurlGlobalGuard {
194         CurlGlobalGuard()
195         {
196             curl_global_init(CURL_GLOBAL_ALL);
197         }
198         ~CurlGlobalGuard()
199         {
200             curl_global_cleanup();
201         }
202     } curlGuard;
203     CURL *curl_handle = curl_easy_init();
204     if (!curl_handle) {
205         return;
206     }
207     struct CurlHandleDeleter {
208         void operator()(CURL *handle)
209         {
210             if (handle)
211                 curl_easy_cleanup(handle);
212         }
213     };
214     std::unique_ptr<CURL, CurlHandleDeleter> handleGuard(curl_handle);
215     std::string data;
216     curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
217     curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteToString);
218     curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &data);
219     curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, USER_AGENT);
220     curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 30L);
221     curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
222     CURLcode res = curl_easy_perform(curl_handle);
223     if (res != CURLE_OK) {
224         NETMGR_LOG_E("CURL perform failed: %{private}s", curl_easy_strerror(res));
225         return;
226     }
227     int responseCode;
228     curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &responseCode);
229     if (responseCode != HTTP_CODE_200) {
230         NETMGR_LOG_E("HTTP request failed with status code: %ld", responseCode);
231         return;
232     }
233     if (data.empty()) {
234         NETMGR_LOG_E("Downloaded PAC script is empty");
235         return;
236     }
237     if (data.find("FindProxyForURL") == std::string::npos) {
238         NETMGR_LOG_W("Downloaded content may not be a valid PAC script (no FindProxyForURL function found)");
239     }
240     return;
241 }
242 
243 
SetFileUrl(const std::string & url)244 void NetPACManager::SetFileUrl(const std::string &url)
245 {
246     scriptFileUrl_ = url;
247 }
248 
ParseHost(const std::string & url)249 std::string NetPACManager::ParseHost(const std::string &url)
250 {
251     CURLU *cu = curl_url();
252     if (!cu) {
253         return EMPTY;
254     }
255     struct CurlUrlDeleter {
256         void operator()(CURLU *handle)
257         {
258             if (handle)
259                 curl_url_cleanup(handle);
260         }
261     };
262     std::unique_ptr<CURLU, CurlUrlDeleter> urlGuard(cu);
263     CURLUcode uc = curl_url_set(cu, CURLUPART_URL, url.c_str(), 0);
264     if (uc != CURLUE_OK) {
265         return EMPTY;
266     }
267     char *host = nullptr;
268     uc = curl_url_get(cu, CURLUPART_HOST, &host, 0);
269     std::string result;
270     if (uc == CURLUE_OK && host) {
271         result = host;
272         curl_free(host);
273     }
274     return result;
275 }
276 } // namespace NetManagerStandard
277 } // namespace OHOS
278 #endif