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