GCC Code Coverage Report


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