GCC Code Coverage Report


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