GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/wpad.cc Lines: 3 142 2.1 %
Date: 2019-02-03 02:48:13 Branches: 4 126 3.2 %

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 "download.h"
18
#include "logging.h"
19
#include "pacparser.h"
20
#include "statistics.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
    download::JobInfo download_pac(&pac_paths[i], false, false, NULL);
177
    int retval = download_manager->Fetch(&download_pac);
178
    if (retval == download::kFailOk) {
179
      string proxies;
180
      retval = ParsePac(download_pac.destination_mem.data,
181
                        download_pac.destination_mem.pos,
182
                        download_manager,
183
                        &proxies);
184
      free(download_pac.destination_mem.data);
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, "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
59
string ResolveProxyDescription(
207
  const string &cvmfs_proxies,
208
  const std::string &path_fallback_cache,
209
  DownloadManager *download_manager)
210
{
211

59
  if ((cvmfs_proxies == "") || (cvmfs_proxies.find("auto") == string::npos))
212
59
    return cvmfs_proxies;
213
214
  bool use_cache = false;
215
  vector<string> lb_groups = SplitString(cvmfs_proxies, ';');
216
  for (unsigned i = 0; i < lb_groups.size(); ++i) {
217
    if (lb_groups[i] != "auto")
218
      continue;
219
220
    lb_groups[i] = AutoProxy(download_manager);
221
    if (lb_groups[i].empty())
222
      use_cache = true;
223
  }
224
225
  string discovered_proxies = JoinStrings(lb_groups, ";");
226
227
  if (!path_fallback_cache.empty()) {
228
    if (use_cache) {
229
      string cached_proxies;
230
      int fd = open(path_fallback_cache.c_str(), O_RDONLY);
231
      if (fd >= 0) {
232
        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
      bool retval =
243
        SafeWriteToFile(discovered_proxies, 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
{
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
  string proxy_configuration = argv[2];
277
  string host_list = argv[3];
278
279
  DownloadManager download_manager;
280
  download_manager.Init(1, false, perf::StatisticsTemplate("pac", &statistics));
281
  download_manager.SetHostChain(host_list);
282
  string resolved_proxies = ResolveProxyDescription(proxy_configuration, "",
283
                                                    &download_manager);
284
  download_manager.Fini();
285
286
  LogCvmfs(kLogDownload, kLogStdout, "%s", resolved_proxies.c_str());
287
  return resolved_proxies == "";
288
}
289
290
}  // namespace download