CernVM-FS  2.13.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
wpad.cc
Go to the documentation of this file.
1 
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 =
109  host_list[i].find_first_of(":/", 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 == "") {
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) {
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) {
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",
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) {
186  "failed to parse pac file %s", pac_paths[i].c_str());
187  } else {
188  if (proxies != "") {
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 string ResolveProxyDescription(const string &cvmfs_proxies,
207  const std::string &path_fallback_cache,
208  DownloadManager *download_manager) {
209  if ((cvmfs_proxies == "") || (cvmfs_proxies.find("auto") == string::npos))
210  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) {
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 =
243  SafeWriteToFile(discovered_proxies, path_fallback_cache, 0660);
244  if (!retval) {
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) {
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 =
282  ResolveProxyDescription(proxy_configuration, "", &download_manager);
283 
284  LogCvmfs(kLogDownload, kLogStdout, "%s", resolved_proxies.c_str());
285  return resolved_proxies == "";
286 }
287 
288 } // namespace download
void SetHostChain(const std::string &host_list)
unsigned char * data()
Definition: sink_mem.h:115
CVMFS_EXPORT const LogSource const int mask
Definition: exception.h:33
CVMFS_EXPORT const LogSource source
Definition: exception.h:33
string JoinStrings(const vector< string > &strings, const string &joint)
Definition: string.cc:356
int MainResolveProxyDescription(int argc, char **argv)
Definition: wpad.cc:268
assert((mem||(size==0))&&"Out Of Memory")
bool SafeWriteToFile(const std::string &content, const std::string &path, int mode)
Definition: posix.cc:2137
void SetAltLogFunc(void(*fn)(const LogSource source, const int mask, const char *msg))
Definition: logging.cc:407
vector< string > SplitString(const string &str, char delim)
Definition: string.cc:306
string AutoProxy(DownloadManager *download_manager)
Definition: wpad.cc:150
LogSource
static void AltCvmfsLogger(const LogSource source, const int mask, const char *msg)
Definition: wpad.cc:256
bool HasPrefix(const string &str, const string &prefix, const bool ignore_case)
Definition: string.cc:279
bool SafeReadToString(int fd, std::string *final_result)
Definition: posix.cc:2117
const char * kAutoPacLocation
Definition: wpad.cc:27
size_t pos()
Definition: sink_mem.h:114
string ResolveProxyDescription(const string &cvmfs_proxies, const std::string &path_fallback_cache, DownloadManager *download_manager)
Definition: wpad.cc:206
Failures Fetch(JobInfo *info)
Definition: download.cc:1984
static bool ParsePac(const char *pac_data, const size_t size, DownloadManager *download_manager, string *proxies)
Definition: wpad.cc:84
static int PrintPacError(const char *fmt, va_list argp)
Definition: wpad.cc:29
void GetHostInfo(std::vector< std::string > *host_chain, std::vector< int > *rtt, unsigned *current_host)
Definition: download.cc:2269
static void size_t size
Definition: smalloc.h:54
static string PacProxy2Cvmfs(const string &pac_proxy, const bool report_errors)
Definition: wpad.cc:41
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:545