squidclient.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2022 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 "time/gadgets.h"
18
19#if _SQUID_WINDOWS_
21using 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 */
61static void usage(const char *progname);
62
63void pipe_handler(int sig);
64static void set_our_signal(void);
65
67
68static int put_fd;
69static char *put_file = nullptr;
70
71static struct stat sb;
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 */
79#endif
80
81#if _SQUID_WINDOWS_
82void
84{
85 WSACleanup();
86 return;
87}
88#endif
89
90static void
91usage(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
131static 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{
184public:
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
197void
198Authorization::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
228static Authorization ProxyAuthorization("Proxy-Authorization", "proxy");
229static Authorization OriginAuthorization("Authorization", "origin server");
230
231int
232main(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 = nullptr;
250 const char *version = "1.0";
251 const char *useragent = nullptr;
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, nullptr, '?'},
271 {"verbose", no_argument, nullptr, 'v'},
272 {"quiet", no_argument, nullptr, 's'},
273 {"host", required_argument, nullptr, 'h'},
274 {"local", required_argument, nullptr, 'l'},
275 {"port", required_argument, nullptr, 'p'},
276 {"ping", no_argument, nullptr, '\1'},
277 {"https", no_argument, nullptr, '\3'},
278 {nullptr, 0, nullptr, 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 Transport::Config.parseCommandOpts(argc, argv, c, optIndex);
297 continue;
298
299 case '\3': // request over a TLS connection
300 Transport::Config.parseCommandOpts(argc, argv, c, optIndex);
301 continue;
302
303 default: // fall through to next switch
304 break;
305 }
306
307 switch (c) {
308
309 case '\0': // dummy value for end-of-options
310 break;
311
312 case 'a':
313 opt_noaccept = 1;
314 break;
315
316 case 'A':
317 useragent = optarg;
318 break;
319
320 case 'j':
321 host = optarg;
322 break;
323
324 case 'V':
325 version = optarg;
326 break;
327
328 case 's': /* silent */
329 to_stdout = false;
330 break;
331
332 case 'k': /* backward compat */
333 keep_alive = 1;
334 break;
335
336 case 'r': /* reload */
337 reload = true;
338 break;
339
340 case 'P':
342 break;
343
344 case 'i': /* IMS */
345 ims = (time_t) atoi(optarg);
346 break;
347
348 case 'm':
349 method = xstrdup(optarg);
350 break;
351
352 case 't':
353 method = xstrdup("TRACE");
354 max_forwards = atoi(optarg);
355 break;
356
357 case 'H':
358 if (strlen(optarg)) {
359 if (extra_hdrs) {
360 std::cerr << "ERROR: multiple -H options not supported. Discarding previous value." << std::endl;
361 xfree(extra_hdrs);
362 }
363 extra_hdrs = xstrdup(optarg);
364 shellUnescape(extra_hdrs);
365 }
366 break;
367
368 case 'T':
370 break;
371
372 case 'u':
374 break;
375
376 case 'w':
378 break;
379
380 case 'U':
382 break;
383
384 case 'W':
386 break;
387
388 case 'n':
389#if HAVE_GSSAPI
390 proxy_neg = 1;
391#else
392 std::cerr << "ERROR: Negotiate authentication not supported." << std::endl;
393 usage(argv[0]);
394#endif
395 break;
396
397 case 'N':
398#if HAVE_GSSAPI
399 www_neg = 1;
400#else
401 std::cerr << "ERROR: Negotiate authentication not supported." << std::endl;
402 usage(argv[0]);
403#endif
404 break;
405
406 case 'v':
407 /* undocumented: may increase verb-level by giving more -v's */
409 debugVerbose(2, "verbosity level set to " << scParams.verbosityLevel);
410 break;
411
412 case '?': /* usage */
413
414 default:
415 usage(argv[0]);
416 break;
417 }
418 }
419 }
420#if _SQUID_WINDOWS_
421 {
422 WSADATA wsaData;
423 WSAStartup(2, &wsaData);
424 atexit(Win32SockCleanup);
425 }
426#endif
427 /* Build the HTTP request */
428 if (strncmp(url, "mgr:", 4) == 0) {
429 char *t = xstrdup(url + 4);
430 const char *at = nullptr;
431 if (!strrchr(t, '@')) { // ignore any -w password if @ is explicit already.
433 }
434 // embed the -w proxy password into old-style cachemgr URLs
435 if (at)
436 snprintf(url, sizeof(url), "cache_object://%s/%s@%s", Transport::Config.hostname, t, at);
437 else
438 snprintf(url, sizeof(url), "cache_object://%s/%s", Transport::Config.hostname, t);
439 xfree(t);
440 }
441 if (put_file) {
442 put_fd = open(put_file, O_RDONLY);
444
445 if (put_fd < 0) {
446 int xerrno = errno;
447 std::cerr << "ERROR: can't open file (" << xstrerr(xerrno) << ")" << std::endl;
448 exit(EXIT_FAILURE);
449 }
450#if _SQUID_WINDOWS_
451 setmode(put_fd, O_BINARY);
452#endif
453
454 if (fstat(put_fd, &sb) < 0) {
455 int xerrno = errno;
456 std::cerr << "ERROR: can't identify length of file (" << xstrerr(xerrno) << ")" << std::endl;
457 }
458 }
459
460 if (!host) {
461 char *newhost = strstr(url, "://");
462 if (newhost) {
463 char *t;
464 newhost += 3;
465 newhost = xstrdup(newhost);
466 t = newhost + strcspn(newhost, "@/?");
467 if (*t == '@') {
468 newhost = t + 1;
469 t = newhost + strcspn(newhost, "@/?");
470 }
471 *t = '\0';
472 host = newhost;
473 }
474 }
475
476 std::stringstream msg;
477
478 if (version[0] == '-' || !version[0]) {
479 /* HTTP/0.9, no headers, no version */
480 msg << method << " " << url << "\r\n";
481 } else {
482 const auto versionImpliesHttp = xisdigit(version[0]); // is HTTP/n.n
483 msg << method << " "
484 << url << " "
485 << (versionImpliesHttp ? "HTTP/" : "") << version
486 << "\r\n";
487
488 if (host) {
489 msg << "Host: " << host << "\r\n";
490 }
491
492 if (!useragent) {
493 msg << "User-Agent: squidclient/" << VERSION << "\r\n";
494 } else if (useragent[0] != '\0') {
495 msg << "User-Agent: " << useragent << "\r\n";
496 } // else custom: no value U-A header
497
498 if (reload) {
499 msg << "Cache-Control: no-cache\r\n";
500 }
501 if (put_fd > 0) {
502 msg << "Content-length: " << sb.st_size << "\r\n";
503 }
504 if (opt_noaccept == 0) {
505 msg << "Accept: */*\r\n";
506 }
507 if (ims) {
508 msg << "If-Modified-Since: " << Time::FormatRfc1123(ims) << "\r\n";
509 }
510 if (max_forwards > -1) {
511 msg << "Max-Forwards: " << max_forwards << "\r\n";
512 }
517#if HAVE_GSSAPI
518 if (www_neg) {
519 if (host) {
520 const char *token = GSSAPI_token(host);
521 msg << "Proxy-Authorization: Negotiate " << token << "\r\n";
522 delete[] token;
523 } else
524 std::cerr << "ERROR: server host missing" << std::endl;
525 }
526 if (proxy_neg) {
527 if (Transport::Config.hostname) {
528 const char *token = GSSAPI_token(Transport::Config.hostname);
529 msg << "Proxy-Authorization: Negotiate " << token << "\r\n";
530 delete[] token;
531 } else
532 std::cerr << "ERROR: proxy server host missing" << std::endl;
533 }
534#endif
535
536 /* HTTP/1.0 may need keep-alive explicitly */
537 if (strcmp(version, "1.0") == 0 && keep_alive)
538 msg << "Connection: keep-alive\r\n";
539
540 /* HTTP/1.1 may need close explicitly */
541 if (!keep_alive)
542 msg << "Connection: close\r\n";
543
544 if (extra_hdrs) {
545 msg << extra_hdrs;
546 safe_free(extra_hdrs);
547 }
548 msg << "\r\n"; // empty line ends MIME header block
549 }
550
551 msg.flush();
552 const auto messageHeader = msg.str();
553 debugVerbose(1, "Request:" << std::endl << messageHeader << std::endl << ".");
554
555 uint32_t loops = Ping::Init();
556
557 for (uint32_t i = 0; loops == 0 || i < loops; ++i) {
558 size_t fsize = 0;
559
560 if (!Transport::Connect())
561 continue;
562
563 /* Send the HTTP request */
564 debugVerbose(2, "Sending HTTP request ... ");
565 bytesWritten = Transport::Write(messageHeader.data(), messageHeader.length());
566
567 if (bytesWritten < 0) {
568 std::cerr << "ERROR: write" << std::endl;
569 exit(EXIT_FAILURE);
570 } else if (static_cast<size_t>(bytesWritten) != messageHeader.length()) {
571 std::cerr << "ERROR: Failed to send the following request: " << std::endl
572 << messageHeader << std::endl;
573 exit(EXIT_FAILURE);
574 }
575 debugVerbose(2, "done.");
576
577 if (put_file) {
578 debugVerbose(1, "Sending HTTP request payload ...");
579 int x;
580 if ((x = lseek(put_fd, 0, SEEK_SET)) < 0) {
581 int xerrno = errno;
582 std::cerr << "ERROR: lseek: " << xstrerr(xerrno) << std::endl;
583
584 } else while ((x = read(put_fd, buf, sizeof(buf))) > 0) {
585
586 x = Transport::Write(buf, x);
587
588 total_bytes += x;
589
590 if (x <= 0)
591 break;
592 }
593
594 if (x != 0)
595 std::cerr << "ERROR: Cannot send file." << std::endl;
596 else
597 debugVerbose(1, "done.");
598 }
599 /* Read the data */
600
601#if _SQUID_WINDOWS_
602 setmode(1, O_BINARY);
603#endif
604
605 while ((len = Transport::Read(buf, sizeof(buf))) > 0) {
606 fsize += len;
607
608 if (to_stdout && fwrite(buf, len, 1, stdout) != 1) {
609 int xerrno = errno;
610 std::cerr << "ERROR: writing to stdout: " << xstrerr(xerrno) << std::endl;
611 }
612 }
613
614#if USE_GNUTLS
615 if (Transport::Config.tlsEnabled) {
616 if (len == 0) {
617 std::cerr << "- Peer has closed the TLS connection" << std::endl;
618 } else if (!gnutls_error_is_fatal(len)) {
619 std::cerr << "WARNING: " << gnutls_strerror(len) << std::endl;
620 } else {
621 std::cerr << "ERROR: " << gnutls_strerror(len) << std::endl;
622 }
623 }
624#endif
625
626#if _SQUID_WINDOWS_
627 setmode(1, O_TEXT);
628#endif
629
631
632 if (Ping::LoopDone(i))
633 break;
634
635 Ping::TimerStop(fsize);
636 }
637
640 return EXIT_SUCCESS;
641}
642
643void
645{
646 std::cerr << "SIGPIPE received." << std::endl;
647}
648
649static void
651{
652#if HAVE_SIGACTION
653 struct sigaction sa;
654 sa.sa_handler = pipe_handler;
655 sa.sa_flags = SA_RESTART;
656 sigemptyset(&sa.sa_mask);
657
658 if (sigaction(SIGPIPE, &sa, nullptr) < 0) {
659 std::cerr << "ERROR: Cannot set PIPE signal." << std::endl;
660 exit(EXIT_FAILURE);
661 }
662#else
663 signal(SIGPIPE, pipe_handler);
664#endif
665}
666
#define debugVerbose(LEVEL, MESSAGE)
display debug messages at varying verbosity levels
Definition: Parameters.h:31
static void Win32SockCleanup(void)
Definition: WinSvc.cc:981
#define assert(EX)
Definition: assert.h:19
void base64_encode_init(struct base64_encode_ctx *ctx)
Definition: base64.c:232
size_t base64_encode_update(struct base64_encode_ctx *ctx, char *dst, size_t length, const uint8_t *src)
Definition: base64.c:265
size_t base64_encode_final(struct base64_encode_ctx *ctx, char *dst)
Definition: base64.c:308
#define base64_encode_len(length)
Definition: base64.h:169
static int version
char progname[]
[Proxy-]Authorization header producer
Definition: squidclient.cc:183
const char * user
user name to encode and send
Definition: squidclient.cc:193
std::string destination
used when describing password
Definition: squidclient.cc:192
std::string header
HTTP header name to send.
Definition: squidclient.cc:191
void commit(std::ostream &os)
finalizes and writes the right HTTP header to the given stream
Definition: squidclient.cc:198
Authorization(const char *aHeader, const char *aDestination)
Definition: squidclient.cc:185
const char * password
user password to encode and send
Definition: squidclient.cc:194
int verbosityLevel
Definition: Parameters.h:27
void usage()
display Ping Options command line help to stderr
Definition: Ping.cc:155
bool parseCommandOpts(int argc, char *argv[], int c, int &optIndex)
Definition: Ping.cc:167
int ioTimeout
I/O operation timeout.
Definition: Transport.h:50
void usage()
display Transport Options command line help to stderr
Definition: Transport.cc:29
bool parseCommandOpts(int argc, char *argv[], int c, int &optIndex)
Definition: Transport.cc:53
Definition: fde.h:52
static fde * Table
global table of FD and their state.
Definition: fde.h:103
#define SA_RESTART
#define O_BINARY
Definition: defines.h:136
#define O_TEXT
Definition: defines.h:133
int optind
Definition: getopt.c:48
char * optarg
Definition: getopt.c:51
static const char * shortOpStr
Definition: main.cc:417
void ProbeTransport(void)
Probe to discover IPv6 capabilities.
bool LoopDone(int i)
whether ping loop is completed at the given iteration.
Definition: Ping.h:45
uint32_t Init()
initialize the squidclient ping mode
Definition: Ping.cc:54
void TimerStop(size_t fsize)
Definition: Ping.cc:92
Ping::TheConfig Config
Definition: Ping.cc:24
void DisplayStats()
display summary of ping data collected
Definition: Ping.cc:141
const char * FormatRfc1123(time_t)
Definition: rfc1123.cc:202
ssize_t Read(void *buf, size_t len)
Definition: Transport.cc:262
TheConfig Config
Definition: Transport.cc:23
void ShutdownTls()
De-initialize TLS library environment when necessary.
Definition: Transport.cc:513
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 CloseConnection()
close the current connection
Definition: Transport.cc:285
#define VERSION
#define xfree
#define xstrdup
static void usage(const char *progname)
Definition: squidclient.cc:91
Parameters scParams
global squidcleint parameters
Definition: squidclient.cc:66
int main(int argc, char *argv[])
Definition: squidclient.cc:232
static struct stat sb
Definition: squidclient.cc:71
static void shellUnescape(char *buf)
Definition: squidclient.cc:132
static int put_fd
Definition: squidclient.cc:68
#define BUFSIZ
Definition: squidclient.cc:57
static void set_our_signal(void)
Definition: squidclient.cc:650
int total_bytes
Definition: squidclient.cc:72
void pipe_handler(int sig)
Definition: squidclient.cc:644
static Authorization OriginAuthorization("Authorization", "origin server")
static Authorization ProxyAuthorization("Proxy-Authorization", "proxy")
static char * put_file
Definition: squidclient.cc:69
#define NULL
Definition: types.h:166
#define safe_free(x)
Definition: xalloc.h:73
#define xisdigit(x)
Definition: xis.h:20
const char * xstrerr(int error)
Definition: xstrerror.cc:83

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors