GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/wpad.cc
Date: 2025-07-13 02:35:07
Exec Total Coverage
Lines: 3 153 2.0%
Branches: 5 297 1.7%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5
6 #include "wpad.h"
7
8 #include <fcntl.h>
9
10 #include <cstdarg>
11 #include <cstdio>
12 #include <cstdlib>
13 #include <string>
14 #include <vector>
15
16 #include "network/download.h"
17 #include "pacparser.h"
18 #include "statistics.h"
19 #include "util/logging.h"
20 #include "util/posix.h"
21 #include "util/string.h"
22
23 using namespace std; // NOLINT
24
25 namespace download {
26
27 const char *kAutoPacLocation = "http://wpad/wpad.dat";
28
29 static int PrintPacError(const char *fmt, va_list argp) {
30 char *msg = NULL;
31
32 const int retval = vasprintf(&msg, fmt, argp);
33 assert(retval != -1); // else: out of memory
34
35 LogCvmfs(kLogDownload, kLogDebug | kLogSyslogErr, "(pacparser) %s", msg);
36 free(msg);
37 return retval;
38 }
39
40
41 static string PacProxy2Cvmfs(const string &pac_proxy,
42 const bool report_errors) {
43 const int log_flags = report_errors ? kLogDebug | kLogSyslogWarn : kLogDebug;
44 if (pac_proxy == "")
45 return "DIRECT";
46
47 string cvmfs_proxy;
48 vector<string> components = SplitString(pac_proxy, ';');
49 for (unsigned i = 0; i < components.size(); ++i) {
50 // Remove white spaces
51 string next_proxy;
52 for (unsigned j = 0; j < components[i].length(); ++j) {
53 if ((components[i][j] != ' ') && (components[i][j] != '\t'))
54 next_proxy.push_back(components[i][j]);
55 }
56
57 // No SOCKS support
58 if (HasPrefix(next_proxy, "SOCKS", false)) {
59 LogCvmfs(kLogDownload, log_flags,
60 "no support for SOCKS proxy, skipping %s",
61 next_proxy.substr(5).c_str());
62 continue;
63 }
64
65 if ((next_proxy != "DIRECT") && !HasPrefix(next_proxy, "PROXY", false)) {
66 LogCvmfs(kLogDownload, log_flags, "invalid proxy definition: %s",
67 next_proxy.c_str());
68 continue;
69 }
70
71 if (HasPrefix(next_proxy, "PROXY", false))
72 next_proxy = next_proxy.substr(5);
73
74 if (cvmfs_proxy == "")
75 cvmfs_proxy = next_proxy;
76 else
77 cvmfs_proxy += ";" + next_proxy;
78 }
79
80 return cvmfs_proxy;
81 }
82
83
84 static bool ParsePac(const char *pac_data, const size_t size,
85 DownloadManager *download_manager, string *proxies) {
86 *proxies = "";
87
88 pacparser_set_error_printer(PrintPacError);
89 bool retval = pacparser_init();
90 if (!retval)
91 return false;
92
93 const string pac_string(pac_data, size);
94 LogCvmfs(kLogDownload, kLogDebug, "PAC script is:\n%s", pac_string.c_str());
95 retval = pacparser_parse_pac_string(pac_string.c_str());
96 if (!retval) {
97 pacparser_cleanup();
98 return false;
99 }
100
101 // For every stratum 1: get proxy
102 vector<string> host_list;
103 vector<int> rtt;
104 unsigned current_host;
105 download_manager->GetHostInfo(&host_list, &rtt, &current_host);
106 for (unsigned i = 0; i < host_list.size(); ++i) {
107 const size_t hostname_begin = 7; // Strip http:// or file://
108 const size_t hostname_end = host_list[i].find_first_of(":/",
109 hostname_begin);
110 const size_t hostname_len = (hostname_end == string::npos)
111 ? string::npos
112 : hostname_end - hostname_begin;
113 const string hostname = (hostname_begin > host_list[i].length())
114 ? "localhost"
115 : host_list[i].substr(hostname_begin,
116 hostname_len);
117 const string url = host_list[i] + "/.cvmfspublished";
118
119 // pac_proxy is freed by JavaScript GC
120 char *pac_proxy = pacparser_find_proxy(url.c_str(), hostname.c_str());
121 if (pac_proxy == NULL) {
122 pacparser_cleanup();
123 return false;
124 }
125 if (*proxies == "") {
126 *proxies = PacProxy2Cvmfs(pac_proxy, true);
127 if (*proxies == "") {
128 LogCvmfs(kLogDownload, kLogDebug | kLogSyslogWarn,
129 "no valid proxy found (%s returned from pac file)", pac_proxy);
130 pacparser_cleanup();
131 return false;
132 }
133 } else {
134 const string alt_proxies = PacProxy2Cvmfs(pac_proxy, false);
135 if (*proxies != alt_proxies) {
136 LogCvmfs(kLogDownload, kLogDebug,
137 "proxy settings for host %s differ from proxy settings for "
138 "other hosts (%s / %s). Not using proxy setting %s.",
139 host_list[i].c_str(), proxies->c_str(), alt_proxies.c_str(),
140 alt_proxies.c_str());
141 }
142 }
143 }
144
145 pacparser_cleanup();
146 return true;
147 }
148
149
150 string AutoProxy(DownloadManager *download_manager) {
151 char *http_env = getenv("http_proxy");
152 if (http_env) {
153 LogCvmfs(kLogDownload, kLogDebug | kLogSyslog,
154 "CernVM-FS: "
155 "using HTTP proxy server(s) %s from http_proxy environment",
156 http_env);
157 return string(http_env);
158 }
159
160 vector<string> pac_paths;
161 char *pac_env = getenv("CVMFS_PAC_URLS");
162 if (pac_env != NULL)
163 pac_paths = SplitString(pac_env, ';');
164
165 // Try downloading from each of the PAC URLs
166 for (unsigned i = 0; i < pac_paths.size(); ++i) {
167 if (pac_paths[i] == "auto") {
168 LogCvmfs(kLogDownload, kLogDebug, "resolving auto proxy config to %s",
169 kAutoPacLocation);
170 pac_paths[i] = string(kAutoPacLocation);
171 }
172 LogCvmfs(kLogDownload, kLogDebug, "looking for proxy config at %s",
173 pac_paths[i].c_str());
174 cvmfs::MemSink pac_memsink;
175 download::JobInfo download_pac(&pac_paths[i], false, false, NULL,
176 &pac_memsink);
177 int retval = download_manager->Fetch(&download_pac);
178 if (retval == download::kFailOk) {
179 string proxies;
180 retval = ParsePac(reinterpret_cast<char *>(pac_memsink.data()),
181 pac_memsink.pos(),
182 download_manager,
183 &proxies);
184 if (!retval) {
185 LogCvmfs(kLogDownload, kLogDebug | kLogSyslogWarn,
186 "failed to parse pac file %s", pac_paths[i].c_str());
187 } else {
188 if (proxies != "") {
189 LogCvmfs(kLogDownload, kLogDebug | kLogSyslog,
190 "CernVM-FS: "
191 "using HTTP proxy server(s) %s from pac file %s",
192 proxies.c_str(), pac_paths[i].c_str());
193 return proxies;
194 }
195 }
196
197 LogCvmfs(kLogDownload, kLogDebug, "no proxy settings found in %s",
198 pac_paths[i].c_str());
199 }
200 }
201
202 return "";
203 }
204
205
206 686 string ResolveProxyDescription(const string &cvmfs_proxies,
207 const std::string &path_fallback_cache,
208 DownloadManager *download_manager) {
209
4/6
✓ Branch 1 taken 394 times.
✓ Branch 2 taken 292 times.
✓ Branch 4 taken 394 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 686 times.
✗ Branch 7 not taken.
686 if ((cvmfs_proxies == "") || (cvmfs_proxies.find("auto") == string::npos))
210
1/2
✓ Branch 1 taken 686 times.
✗ Branch 2 not taken.
686 return cvmfs_proxies;
211
212 int empty_auto = -1;
213 vector<string> lb_groups = SplitString(cvmfs_proxies, ';');
214 for (unsigned i = 0; i < lb_groups.size(); ++i) {
215 if (lb_groups[i] != "auto")
216 continue;
217
218 lb_groups[i] = AutoProxy(download_manager);
219 if (lb_groups[i].empty())
220 empty_auto = static_cast<int>(i);
221 }
222
223 if (empty_auto != -1)
224 lb_groups.erase(lb_groups.begin() + static_cast<unsigned>(empty_auto));
225 string discovered_proxies = JoinStrings(lb_groups, ";");
226
227 if (!path_fallback_cache.empty()) {
228 if (empty_auto != -1) {
229 string cached_proxies;
230 const int fd = open(path_fallback_cache.c_str(), O_RDONLY);
231 if (fd >= 0) {
232 const bool retval = SafeReadToString(fd, &cached_proxies);
233 close(fd);
234 if (retval) {
235 LogCvmfs(kLogDownload, kLogSyslog | kLogDebug,
236 "using cached proxy settings from %s",
237 path_fallback_cache.c_str());
238 return cached_proxies;
239 }
240 }
241 } else {
242 const bool retval = SafeWriteToFile(discovered_proxies,
243 path_fallback_cache, 0660);
244 if (!retval) {
245 LogCvmfs(kLogDownload, kLogSyslogWarn | kLogDebug,
246 "failed to write proxy settings into %s",
247 path_fallback_cache.c_str());
248 }
249 }
250 }
251
252 return discovered_proxies;
253 }
254
255
256 static void AltCvmfsLogger(const LogSource source, const int mask,
257 const char *msg) {
258 FILE *log_output = NULL;
259 if (mask & kLogStdout)
260 log_output = stdout;
261 else if (mask & kLogStderr || mask & kLogSyslogWarn || mask & kLogSyslogErr)
262 log_output = stderr;
263 if (log_output)
264 fprintf(log_output, "%s\n", msg);
265 }
266
267
268 int MainResolveProxyDescription(int argc, char **argv) {
269 SetAltLogFunc(AltCvmfsLogger);
270 if (argc < 4) {
271 LogCvmfs(kLogDownload, kLogStderr, "arguments missing");
272 return 1;
273 }
274 perf::Statistics statistics;
275 const string proxy_configuration = argv[2];
276 const string host_list = argv[3];
277
278 DownloadManager download_manager(
279 1, perf::StatisticsTemplate("pac", &statistics));
280 download_manager.SetHostChain(host_list);
281 const string resolved_proxies = ResolveProxyDescription(
282 proxy_configuration, "", &download_manager);
283
284 LogCvmfs(kLogDownload, kLogStdout, "%s", resolved_proxies.c_str());
285 return resolved_proxies == "";
286 }
287
288 } // namespace download
289