squidclient.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2019 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 #include "squid.h"
10 #include "base64.h"
11 #include "ip/Address.h"
12 #include "ip/tools.h"
13 #include "rfc1123.h"
16 #include "tools/squidclient/Ping.h"
18 
19 #if _SQUID_WINDOWS_
20 
21 using namespace Squid;
23 #endif
24 
25 #include <cassert>
26 #include <cerrno>
27 #include <csignal>
28 #include <cstring>
29 #include <iostream>
30 #include <sstream>
31 #if _SQUID_WINDOWS_
32 #include <io.h>
33 #endif
34 #if HAVE_SYS_SOCKET_H
35 #include <sys/socket.h>
36 #endif
37 #if HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40 #if HAVE_NETDB_H
41 #include <netdb.h>
42 #endif
43 #if HAVE_SYS_STAT_H
44 #include <sys/stat.h>
45 #endif
46 #if HAVE_FCNTL_H
47 #include <fcntl.h>
48 #endif
49 #if HAVE_NETINET_IN_H
50 #include <netinet/in.h>
51 #endif
52 #if HAVE_GETOPT_H
53 #include <getopt.h>
54 #endif
55 
56 #ifndef BUFSIZ
57 #define BUFSIZ 8192
58 #endif
59 
60 /* Local functions */
61 static void usage(const char *progname);
62 
63 void pipe_handler(int sig);
64 static void set_our_signal(void);
65 
67 
68 static int put_fd;
69 static char *put_file = NULL;
70 
71 static struct stat sb;
72 int total_bytes = 0;
73 
74 #if _SQUID_AIX_
75 /* Bug 3854: AIX 6.1 tries to link in this fde.h global symbol
76  * despite squidclient not using any of the fd_* code.
77  */
78 fde *fde::Table = NULL;
79 #endif
80 
81 #if _SQUID_WINDOWS_
82 void
83 Win32SockCleanup(void)
84 {
85  WSACleanup();
86  return;
87 }
88 #endif
89 
90 static void
91 usage(const char *progname)
92 {
93  std::cerr << "Version: " << VERSION << std::endl
94  << "Usage: " << progname << " [Basic Options] [HTTP Options]" << std::endl
95  << std::endl;
96  std::cerr
97  << " -s | --quiet Silent. Do not print response message to stdout." << std::endl
98  << " -v | --verbose Verbose debugging. Repeat (-vv) to increase output level." << std::endl
99  << " Levels:" << std::endl
100  << " 1 - Print outgoing request message to stderr." << std::endl
101  << " 2 - Print action trace to stderr." << std::endl
102  << " --help Display this help text." << std::endl
103  << std::endl;
106  std::cerr
107  << "HTTP Options:" << std::endl
108  << " -a Do NOT include Accept: header." << std::endl
109  << " -A User-Agent: header. Use \"\" to omit." << std::endl
110  << " -H 'string' Extra headers to send. Supports '\\\\', '\\n', '\\r' and '\\t'." << std::endl
111  << " -i IMS If-Modified-Since time (in Epoch seconds)." << std::endl
112  << " -j hosthdr Host header content" << std::endl
113  << " -k Keep the connection active. Default is to do only one request then close." << std::endl
114  << " -m method Request method, default is GET." << std::endl
115 #if HAVE_GSSAPI
116  << " -n Proxy Negotiate(Kerberos) authentication" << std::endl
117  << " -N WWW Negotiate(Kerberos) authentication" << std::endl
118 #endif
119  << " -P file Send content from the named file as request payload" << std::endl
120  << " -r Force cache to reload URL" << std::endl
121  << " -t count Trace count cache-hops" << std::endl
122  << " -u user Proxy authentication username" << std::endl
123  << " -U user WWW authentication username" << std::endl
124  << " -V version HTTP Version. Use '-' for HTTP/0.9 omitted case" << std::endl
125  << " -w password Proxy authentication password" << std::endl
126  << " -W password WWW authentication password" << std::endl
127  ;
128  exit(EXIT_FAILURE);
129 }
130 
131 static void
133 {
134  if (!buf)
135  return;
136 
137  unsigned char *p, *d;
138 
139  d = p = reinterpret_cast<unsigned char *>(buf);
140 
141  while (auto ch = *p) {
142 
143  if (ch == '\\') {
144  ++p;
145 
146  switch (*p) {
147  case 'n':
148  ch = '\n';
149  break;
150  case 'r':
151  ch = '\r';
152  break;
153  case 't':
154  ch = '\t';
155  break;
156  case '\\':
157  ch = '\\';
158  break;
159  default:
160  ch = *p;
161  debugVerbose(1, "Warning: unsupported shell code '\\" << ch << "'");
162  break;
163  }
164 
165  *d = ch;
166 
167  if (!ch)
168  continue;
169 
170  } else {
171  *d = *p;
172  }
173 
174  ++p;
175  ++d;
176  }
177 
178  *d = '\0';
179 }
180 
183 {
184 public:
185  Authorization(const char *aHeader, const char *aDestination):
186  header(aHeader), destination(aDestination) {}
187 
189  void commit(std::ostream &os);
190 
191  std::string header;
192  std::string destination;
193  const char *user = nullptr;
194  const char *password = nullptr;
195 };
196 
197 void
198 Authorization::commit(std::ostream &os)
199 {
200 #if HAVE_GETPASS
201  if (!password)
202  password = getpass((destination + " password: ").c_str());
203 #endif
204  if (!password) {
205  std::cerr << "ERROR: " << destination << " password missing\n";
206  exit(EXIT_FAILURE);
207  }
208 
209  struct base64_encode_ctx ctx;
210  base64_encode_init(&ctx);
211  const auto bcapacity = base64_encode_len(strlen(user) + 1 + strlen(password));
212  const auto buf = new char[bcapacity];
213 
214  size_t bsize = 0;
215  bsize += base64_encode_update(&ctx, buf, strlen(user), reinterpret_cast<const uint8_t*>(user));
216  bsize += base64_encode_update(&ctx, buf+bsize, 1, reinterpret_cast<const uint8_t*>(":"));
217  bsize += base64_encode_update(&ctx, buf+bsize, strlen(password), reinterpret_cast<const uint8_t*>(password));
218  bsize += base64_encode_final(&ctx, buf+bsize);
219  assert(bsize <= bcapacity); // paranoid and late but better than nothing
220 
221  os << header << ": Basic ";
222  os.write(buf, bsize);
223  os << "\r\n";
224 
225  delete[] buf;
226 }
227 
228 static Authorization ProxyAuthorization("Proxy-Authorization", "proxy");
229 static Authorization OriginAuthorization("Authorization", "origin server");
230 
231 int
232 main(int argc, char *argv[])
233 {
234  int len, bytesWritten;
235  bool to_stdout, reload;
236  int keep_alive = 0;
237  int opt_noaccept = 0;
238 #if HAVE_GSSAPI
239  int www_neg = 0, proxy_neg = 0;
240 #endif
241  char url[BUFSIZ];
242  char buf[BUFSIZ];
243  char *extra_hdrs = nullptr;
244  const char *method = "GET";
245  extern char *optarg;
246  time_t ims = 0;
247  int max_forwards = -1;
248 
249  const char *host = NULL;
250  const char *version = "1.0";
251  const char *useragent = NULL;
252 
253  /* set the defaults */
254  to_stdout = true;
255  reload = false;
256 
257  Ip::ProbeTransport(); // determine IPv4 or IPv6 capabilities before parsing.
258  if (argc < 2 || argv[argc-1][0] == '-') {
259  usage(argv[0]); /* need URL */
260  } else if (argc >= 2) {
261  strncpy(url, argv[argc - 1], sizeof(url));
262  url[sizeof(url) - 1] = '\0';
263 
264  int optIndex = 0;
265  const char *shortOpStr = "aA:h:j:V:l:P:i:km:nNp:rsvt:H:T:u:U:w:W:?";
266 
267  // options for controlling squidclient
268  static struct option basicOptions[] = {
269  /* These are the generic options for squidclient itself */
270  {"help", no_argument, 0, '?'},
271  {"verbose", no_argument, 0, 'v'},
272  {"quiet", no_argument, 0, 's'},
273  {"host", required_argument, 0, 'h'},
274  {"local", required_argument, 0, 'l'},
275  {"port", required_argument, 0, 'p'},
276  {"ping", no_argument, 0, '\1'},
277  {"https", no_argument, 0, '\3'},
278  {0, 0, 0, 0}
279  };
280 
281  int c;
282  while ((c = getopt_long(argc, argv, shortOpStr, basicOptions, &optIndex)) != -1) {
283 
284  // modules parse their own specific options
285  switch (c) {
286  case '\1':
287  to_stdout = 0;
288  Ping::Config.parseCommandOpts(argc, argv, c, optIndex);
289  continue;
290 
291  case 'h': /* remote host */
292  case 'l': /* local host */
293  case 'p': /* port number */
294  // rewind and let the Transport::Config parser handle
295  optind -= 2;
296 
297  case '\3': // request over a TLS connection
298  Transport::Config.parseCommandOpts(argc, argv, c, optIndex);
299  continue;
300 
301  default: // fall through to next switch
302  break;
303  }
304 
305  switch (c) {
306 
307  case '\0': // dummy value for end-of-options
308  break;
309 
310  case 'a':
311  opt_noaccept = 1;
312  break;
313 
314  case 'A':
315  useragent = optarg;
316  break;
317 
318  case 'j':
319  host = optarg;
320  break;
321 
322  case 'V':
323  version = optarg;
324  break;
325 
326  case 's': /* silent */
327  to_stdout = false;
328  break;
329 
330  case 'k': /* backward compat */
331  keep_alive = 1;
332  break;
333 
334  case 'r': /* reload */
335  reload = true;
336  break;
337 
338  case 'P':
339  put_file = xstrdup(optarg);
340  break;
341 
342  case 'i': /* IMS */
343  ims = (time_t) atoi(optarg);
344  break;
345 
346  case 'm':
347  method = xstrdup(optarg);
348  break;
349 
350  case 't':
351  method = xstrdup("TRACE");
352  max_forwards = atoi(optarg);
353  break;
354 
355  case 'H':
356  if (strlen(optarg)) {
357  if (extra_hdrs) {
358  std::cerr << "ERROR: multiple -H options not supported. Discarding previous value." << std::endl;
359  xfree(extra_hdrs);
360  }
361  extra_hdrs = xstrdup(optarg);
362  shellUnescape(extra_hdrs);
363  }
364  break;
365 
366  case 'T':
367  Transport::Config.ioTimeout = atoi(optarg);
368  break;
369 
370  case 'u':
371  ProxyAuthorization.user = optarg;
372  break;
373 
374  case 'w':
375  ProxyAuthorization.password = optarg;
376  break;
377 
378  case 'U':
379  OriginAuthorization.user = optarg;
380  break;
381 
382  case 'W':
383  OriginAuthorization.password = optarg;
384  break;
385 
386  case 'n':
387 #if HAVE_GSSAPI
388  proxy_neg = 1;
389 #else
390  std::cerr << "ERROR: Negotiate authentication not supported." << std::endl;
391  usage(argv[0]);
392 #endif
393  break;
394 
395  case 'N':
396 #if HAVE_GSSAPI
397  www_neg = 1;
398 #else
399  std::cerr << "ERROR: Negotiate authentication not supported." << std::endl;
400  usage(argv[0]);
401 #endif
402  break;
403 
404  case 'v':
405  /* undocumented: may increase verb-level by giving more -v's */
406  ++scParams.verbosityLevel;
407  debugVerbose(2, "verbosity level set to " << scParams.verbosityLevel);
408  break;
409 
410  case '?': /* usage */
411 
412  default:
413  usage(argv[0]);
414  break;
415  }
416  }
417  }
418 #if _SQUID_WINDOWS_
419  {
420  WSADATA wsaData;
421  WSAStartup(2, &wsaData);
422  atexit(Win32SockCleanup);
423  }
424 #endif
425  /* Build the HTTP request */
426  if (strncmp(url, "mgr:", 4) == 0) {
427  char *t = xstrdup(url + 4);
428  const char *at = NULL;
429  if (!strrchr(t, '@')) { // ignore any -w password if @ is explicit already.
430  at = ProxyAuthorization.password;
431  }
432  // embed the -w proxy password into old-style cachemgr URLs
433  if (at)
434  snprintf(url, sizeof(url), "cache_object://%s/%s@%s", Transport::Config.hostname, t, at);
435  else
436  snprintf(url, sizeof(url), "cache_object://%s/%s", Transport::Config.hostname, t);
437  xfree(t);
438  }
439  if (put_file) {
440  put_fd = open(put_file, O_RDONLY);
441  set_our_signal();
442 
443  if (put_fd < 0) {
444  int xerrno = errno;
445  std::cerr << "ERROR: can't open file (" << xstrerr(xerrno) << ")" << std::endl;
446  exit(EXIT_FAILURE);
447  }
448 #if _SQUID_WINDOWS_
449  setmode(put_fd, O_BINARY);
450 #endif
451 
452  if (fstat(put_fd, &sb) < 0) {
453  int xerrno = errno;
454  std::cerr << "ERROR: can't identify length of file (" << xstrerr(xerrno) << ")" << std::endl;
455  }
456  }
457 
458  if (!host) {
459  char *newhost = strstr(url, "://");
460  if (newhost) {
461  char *t;
462  newhost += 3;
463  newhost = xstrdup(newhost);
464  t = newhost + strcspn(newhost, "@/?");
465  if (*t == '@') {
466  newhost = t + 1;
467  t = newhost + strcspn(newhost, "@/?");
468  }
469  *t = '\0';
470  host = newhost;
471  }
472  }
473 
474  std::stringstream msg;
475 
476  if (version[0] == '-' || !version[0]) {
477  /* HTTP/0.9, no headers, no version */
478  msg << method << " " << url << "\r\n";
479  } else {
480  const auto versionImpliesHttp = xisdigit(version[0]); // is HTTP/n.n
481  msg << method << " "
482  << url << " "
483  << (versionImpliesHttp ? "HTTP/" : "") << version
484  << "\r\n";
485 
486  if (host) {
487  msg << "Host: " << host << "\r\n";
488  }
489 
490  if (!useragent) {
491  msg << "User-Agent: squidclient/" << VERSION << "\r\n";
492  } else if (useragent[0] != '\0') {
493  msg << "User-Agent: " << useragent << "\r\n";
494  } // else custom: no value U-A header
495 
496  if (reload) {
497  msg << "Cache-Control: no-cache\r\n";
498  }
499  if (put_fd > 0) {
500  msg << "Content-length: " << sb.st_size << "\r\n";
501  }
502  if (opt_noaccept == 0) {
503  msg << "Accept: */*\r\n";
504  }
505  if (ims) {
506  msg << "If-Modified-Since: " << mkrfc1123(ims) << "\r\n";
507  }
508  if (max_forwards > -1) {
509  msg << "Max-Forwards: " << max_forwards << "\r\n";
510  }
511  if (ProxyAuthorization.user)
512  ProxyAuthorization.commit(msg);
513  if (OriginAuthorization.user)
514  OriginAuthorization.commit(msg);
515 #if HAVE_GSSAPI
516  if (www_neg) {
517  if (host) {
518  const char *token = GSSAPI_token(host);
519  msg << "Proxy-Authorization: Negotiate " << token << "\r\n";
520  delete[] token;
521  } else
522  std::cerr << "ERROR: server host missing" << std::endl;
523  }
524  if (proxy_neg) {
525  if (Transport::Config.hostname) {
526  const char *token = GSSAPI_token(Transport::Config.hostname);
527  msg << "Proxy-Authorization: Negotiate " << token << "\r\n";
528  delete[] token;
529  } else
530  std::cerr << "ERROR: proxy server host missing" << std::endl;
531  }
532 #endif
533 
534  /* HTTP/1.0 may need keep-alive explicitly */
535  if (strcmp(version, "1.0") == 0 && keep_alive)
536  msg << "Connection: keep-alive\r\n";
537 
538  /* HTTP/1.1 may need close explicitly */
539  if (!keep_alive)
540  msg << "Connection: close\r\n";
541 
542  if (extra_hdrs) {
543  msg << extra_hdrs;
544  safe_free(extra_hdrs);
545  }
546  msg << "\r\n"; // empty line ends MIME header block
547  }
548 
549  msg.flush();
550  const auto messageHeader = msg.str();
551  debugVerbose(1, "Request:" << std::endl << messageHeader << std::endl << ".");
552 
553  uint32_t loops = Ping::Init();
554 
555  for (uint32_t i = 0; loops == 0 || i < loops; ++i) {
556  size_t fsize = 0;
557 
558  if (!Transport::Connect())
559  continue;
560 
561  /* Send the HTTP request */
562  debugVerbose(2, "Sending HTTP request ... ");
563  bytesWritten = Transport::Write(messageHeader.data(), messageHeader.length());
564 
565  if (bytesWritten < 0) {
566  std::cerr << "ERROR: write" << std::endl;
567  exit(EXIT_FAILURE);
568  } else if (static_cast<size_t>(bytesWritten) != messageHeader.length()) {
569  std::cerr << "ERROR: Failed to send the following request: " << std::endl
570  << messageHeader << std::endl;
571  exit(EXIT_FAILURE);
572  }
573  debugVerbose(2, "done.");
574 
575  if (put_file) {
576  debugVerbose(1, "Sending HTTP request payload ...");
577  int x;
578  if ((x = lseek(put_fd, 0, SEEK_SET)) < 0) {
579  int xerrno = errno;
580  std::cerr << "ERROR: lseek: " << xstrerr(xerrno) << std::endl;
581 
582  } else while ((x = read(put_fd, buf, sizeof(buf))) > 0) {
583 
584  x = Transport::Write(buf, x);
585 
586  total_bytes += x;
587 
588  if (x <= 0)
589  break;
590  }
591 
592  if (x != 0)
593  std::cerr << "ERROR: Cannot send file." << std::endl;
594  else
595  debugVerbose(1, "done.");
596  }
597  /* Read the data */
598 
599 #if _SQUID_WINDOWS_
600  setmode(1, O_BINARY);
601 #endif
602 
603  while ((len = Transport::Read(buf, sizeof(buf))) > 0) {
604  fsize += len;
605 
606  if (to_stdout && fwrite(buf, len, 1, stdout) != 1) {
607  int xerrno = errno;
608  std::cerr << "ERROR: writing to stdout: " << xstrerr(xerrno) << std::endl;
609  }
610  }
611 
612 #if USE_GNUTLS
613  if (Transport::Config.tlsEnabled) {
614  if (len == 0) {
615  std::cerr << "- Peer has closed the TLS connection" << std::endl;
616  } else if (!gnutls_error_is_fatal(len)) {
617  std::cerr << "WARNING: " << gnutls_strerror(len) << std::endl;
618  } else {
619  std::cerr << "ERROR: " << gnutls_strerror(len) << std::endl;
620  }
621  }
622 #endif
623 
624 #if _SQUID_WINDOWS_
625  setmode(1, O_TEXT);
626 #endif
627 
629 
630  if (Ping::LoopDone(i))
631  break;
632 
633  Ping::TimerStop(fsize);
634  }
635 
638  return EXIT_SUCCESS;
639 }
640 
641 void
643 {
644  std::cerr << "SIGPIPE received." << std::endl;
645 }
646 
647 static void
649 {
650 #if HAVE_SIGACTION
651  struct sigaction sa;
652  sa.sa_handler = pipe_handler;
653  sa.sa_flags = SA_RESTART;
654  sigemptyset(&sa.sa_mask);
655 
656  if (sigaction(SIGPIPE, &sa, NULL) < 0) {
657  std::cerr << "ERROR: Cannot set PIPE signal." << std::endl;
658  exit(EXIT_FAILURE);
659  }
660 #else
661  signal(SIGPIPE, pipe_handler);
662 #endif
663 }
664 
[Proxy-]Authorization header producer
Definition: squidclient.cc:182
int ioTimeout
I/O operation timeout.
Definition: Transport.h:50
Ping::TheConfig Config
Definition: Ping.cc:24
bool parseCommandOpts(int argc, char *argv[], int c, int &optIndex)
Definition: Transport.cc:53
#define assert(EX)
Definition: assert.h:17
static char * put_file
Definition: squidclient.cc:69
const char * user
user name to encode and send
Definition: squidclient.cc:193
const char * mkrfc1123(time_t)
Definition: rfc1123.c:202
int main(int argc, char *argv[])
Definition: squidclient.cc:232
static Authorization ProxyAuthorization("Proxy-Authorization", "proxy")
void CloseConnection()
close the current connection
Definition: Transport.cc:285
int i
Definition: membanger.c:49
#define xstrdup
static int version
TheConfig Config
Definition: Transport.cc:23
#define safe_free(x)
Definition: xalloc.h:73
static struct stat sb
Definition: squidclient.cc:71
void commit(std::ostream &os)
finalizes and writes the right HTTP header to the given stream
Definition: squidclient.cc:198
char * p
Definition: membanger.c:43
void usage()
display Ping Options command line help to stderr
Definition: Ping.cc:155
void usage()
display Transport Options command line help to stderr
Definition: Transport.cc:29
#define BUFSIZ
Definition: squidclient.cc:57
std::string destination
used when describing password
Definition: squidclient.cc:192
int verbosityLevel
Definition: Parameters.h:27
static void shellUnescape(char *buf)
Definition: squidclient.cc:132
const char * xstrerr(int error)
Definition: xstrerror.cc:83
void pipe_handler(int sig)
Definition: squidclient.cc:642
static void usage(const char *progname)
Definition: squidclient.cc:91
const char * password
user password to encode and send
Definition: squidclient.cc:194
bool LoopDone(int i)
whether ping loop is completed at the given iteration.
Definition: Ping.h:45
static int put_fd
Definition: squidclient.cc:68
ssize_t Write(const void *buf, size_t len)
Definition: Transport.cc:238
bool Connect()
locate and connect to the configured server
Definition: Transport.cc:214
void base64_encode_init(struct base64_encode_ctx *ctx)
Definition: base64.c:232
#define debugVerbose(LEVEL, MESSAGE)
display debug messages at varying verbosity levels
Definition: Parameters.h:31
void ProbeTransport(void)
Probe to discover IPv6 capabilities.
uint32_t Init()
initialize the squidclient ping mode
Definition: Ping.cc:54
int unsigned int const char *desc STUB void int len
Definition: stub_fd.cc:20
void TimerStop(size_t fsize)
Definition: Ping.cc:92
void const char * buf
Definition: stub_helper.cc:16
#define SA_RESTART
#define O_TEXT
Definition: defines.h:201
Authorization(const char *aHeader, const char *aDestination)
Definition: squidclient.cc:185
size_t base64_encode_final(struct base64_encode_ctx *ctx, char *dst)
Definition: base64.c:308
int optind
Definition: getopt.c:48
static Authorization OriginAuthorization("Authorization", "origin server")
ssize_t Read(void *buf, size_t len)
Definition: Transport.cc:262
static void Win32SockCleanup(void)
Definition: WinSvc.cc:981
std::string header
HTTP header name to send.
Definition: squidclient.cc:191
#define xisdigit(x)
Definition: xis.h:20
size_t base64_encode_update(struct base64_encode_ctx *ctx, char *dst, size_t length, const uint8_t *src)
Definition: base64.c:265
Parameters scParams
global squidcleint parameters
Definition: squidclient.cc:66
#define VERSION
size_t HttpReply *STUB StoreEntry const KeyScope scope const HttpRequestMethod & method
Definition: stub_store.cc:112
void DisplayStats()
display summary of ping data collected
Definition: Ping.cc:141
Definition: fde.h:49
static void set_our_signal(void)
Definition: squidclient.cc:648
void ShutdownTls()
De-initialize TLS library environment when necessary.
Definition: Transport.cc:511
#define O_BINARY
Definition: defines.h:204
#define xfree
int total_bytes
Definition: squidclient.cc:72
static const char * shortOpStr
Definition: main.cc:423
char * optarg
Definition: getopt.c:51
#define NULL
Definition: types.h:166
char progname[]
bool parseCommandOpts(int argc, char *argv[], int c, int &optIndex)
Definition: Ping.cc:167
#define base64_encode_len(length)
Definition: base64.h:169

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors