Directory: | cvmfs/ |
---|---|
File: | cvmfs/wpad.cc |
Date: | 2025-02-09 02:34:19 |
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 | |||
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, ¤t_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 |