]>
git.ipfire.org Git - thirdparty/squid.git/blob - tools/squidclient/squidclient.cc
e73ef2bf0e5c40a7bed4b8d9e04da8934c20c4a2
2 * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
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.
11 #include "ip/Address.h"
14 #include "tools/squidclient/gssapi_support.h"
15 #include "tools/squidclient/Parameters.h"
16 #include "tools/squidclient/Ping.h"
17 #include "tools/squidclient/Transport.h"
20 /** \cond AUTODOCS-IGNORE */
21 using namespace Squid
;
33 #include <sys/socket.h>
48 #include <netinet/in.h>
58 #define MESSAGELEN 65536
61 #define HEADERLEN 65536
65 static void usage(const char *progname
);
67 void pipe_handler(int sig
);
68 static void set_our_signal(void);
73 static char *put_file
= NULL
;
75 static struct stat sb
;
79 /* Bug 3854: AIX 6.1 tries to link in this fde.h global symbol
80 * despite squidclient not using any of the fd_* code.
82 fde
*fde::Table
= NULL
;
87 Win32SockCleanup(void)
95 usage(const char *progname
)
97 std::cerr
<< "Version: " << VERSION
<< std::endl
98 << "Usage: " << progname
<< " [Basic Options] [HTTP Options]" << std::endl
101 << " -s | --quiet Silent. Do not print response message to stdout." << std::endl
102 << " -v | --verbose Verbose debugging. Repeat (-vv) to increase output level." << std::endl
103 << " Levels:" << std::endl
104 << " 1 - Print outgoing request message to stderr." << std::endl
105 << " 2 - Print action trace to stderr." << std::endl
106 << " --help Display this help text." << std::endl
108 Transport::Config
.usage();
109 Ping::Config
.usage();
111 << "HTTP Options:" << std::endl
112 << " -a Do NOT include Accept: header." << std::endl
113 << " -A User-Agent: header. Use \"\" to omit." << std::endl
114 << " -H 'string' Extra headers to send. Supports '\\\\', '\\n', '\\r' and '\\t'." << std::endl
115 << " -i IMS If-Modified-Since time (in Epoch seconds)." << std::endl
116 << " -j hosthdr Host header content" << std::endl
117 << " -k Keep the connection active. Default is to do only one request then close." << std::endl
118 << " -m method Request method, default is GET." << std::endl
120 << " -n Proxy Negotiate(Kerberos) authentication" << std::endl
121 << " -N WWW Negotiate(Kerberos) authentication" << std::endl
123 << " -P file Send content from the named file as request payload" << std::endl
124 << " -r Force cache to reload URL" << std::endl
125 << " -t count Trace count cache-hops" << std::endl
126 << " -u user Proxy authentication username" << std::endl
127 << " -U user WWW authentication username" << std::endl
128 << " -V version HTTP Version. Use '-' for HTTP/0.9 omitted case" << std::endl
129 << " -w password Proxy authentication password" << std::endl
130 << " -W password WWW authentication password" << std::endl
136 shellUnescape(char *buf
)
141 unsigned char *p
, *d
;
143 d
= p
= reinterpret_cast<unsigned char *>(buf
);
145 while (auto ch
= *p
) {
165 debugVerbose(1, "Warning: unsupported shell code '\\" << ch
<< "'");
186 main(int argc
, char *argv
[])
188 int len
, bytesWritten
;
189 bool to_stdout
, reload
;
191 int opt_noaccept
= 0;
193 int www_neg
= 0, proxy_neg
= 0;
195 char url
[BUFSIZ
], msg
[MESSAGELEN
], buf
[BUFSIZ
];
196 char extra_hdrs
[HEADERLEN
];
197 const char *method
= "GET";
200 int max_forwards
= -1;
202 const char *proxy_user
= NULL
;
203 const char *proxy_password
= NULL
;
204 const char *www_user
= NULL
;
205 const char *www_password
= NULL
;
206 const char *host
= NULL
;
207 const char *version
= "1.0";
208 const char *useragent
= NULL
;
210 /* set the defaults */
211 extra_hdrs
[0] = '\0';
215 Ip::ProbeTransport(); // determine IPv4 or IPv6 capabilities before parsing.
216 if (argc
< 2 || argv
[argc
-1][0] == '-') {
217 usage(argv
[0]); /* need URL */
218 } else if (argc
>= 2) {
219 strncpy(url
, argv
[argc
- 1], BUFSIZ
);
220 url
[BUFSIZ
- 1] = '\0';
223 const char *shortOpStr
= "aA:h:j:V:l:P:i:km:nNp:rsvt:H:T:u:U:w:W:?";
225 // options for controlling squidclient
226 static struct option basicOptions
[] = {
227 /* These are the generic options for squidclient itself */
228 {"help", no_argument
, 0, '?'},
229 {"verbose", no_argument
, 0, 'v'},
230 {"quiet", no_argument
, 0, 's'},
231 {"host", required_argument
, 0, 'h'},
232 {"local", required_argument
, 0, 'l'},
233 {"port", required_argument
, 0, 'p'},
234 {"ping", no_argument
, 0, '\1'},
235 {"https", no_argument
, 0, '\3'},
240 while ((c
= getopt_long(argc
, argv
, shortOpStr
, basicOptions
, &optIndex
)) != -1) {
242 // modules parse their own specific options
246 Ping::Config
.parseCommandOpts(argc
, argv
, c
, optIndex
);
249 case 'h': /* remote host */
250 case 'l': /* local host */
251 case 'p': /* port number */
252 // rewind and let the Transport::Config parser handle
255 case '\3': // request over a TLS connection
256 Transport::Config
.parseCommandOpts(argc
, argv
, c
, optIndex
);
259 default: // fall through to next switch
265 case '\0': // dummy value for end-of-options
284 case 's': /* silent */
288 case 'k': /* backward compat */
292 case 'r': /* reload */
297 put_file
= xstrdup(optarg
);
301 ims
= (time_t) atoi(optarg
);
305 method
= xstrdup(optarg
);
309 method
= xstrdup("TRACE");
310 max_forwards
= atoi(optarg
);
314 if (strlen(optarg
)) {
315 strncpy(extra_hdrs
, optarg
, sizeof(extra_hdrs
));
316 shellUnescape(extra_hdrs
);
321 Transport::Config
.ioTimeout
= atoi(optarg
);
329 proxy_password
= optarg
;
337 www_password
= optarg
;
344 std::cerr
<< "ERROR: Negotiate authentication not supported." << std::endl
;
353 std::cerr
<< "ERROR: Negotiate authentication not supported." << std::endl
;
359 /* undocumented: may increase verb-level by giving more -v's */
360 ++scParams
.verbosityLevel
;
361 debugVerbose(2, "verbosity level set to " << scParams
.verbosityLevel
);
364 case '?': /* usage */
375 WSAStartup(2, &wsaData
);
376 atexit(Win32SockCleanup
);
379 /* Build the HTTP request */
380 if (strncmp(url
, "mgr:", 4) == 0) {
381 char *t
= xstrdup(url
+ 4);
382 const char *at
= NULL
;
383 if (!strrchr(t
, '@')) { // ignore any -w password if @ is explicit already.
386 // embed the -w proxy password into old-style cachemgr URLs
388 snprintf(url
, BUFSIZ
, "cache_object://%s/%s@%s", Transport::Config
.hostname
, t
, at
);
390 snprintf(url
, BUFSIZ
, "cache_object://%s/%s", Transport::Config
.hostname
, t
);
394 put_fd
= open(put_file
, O_RDONLY
);
399 std::cerr
<< "ERROR: can't open file (" << xstrerr(xerrno
) << ")" << std::endl
;
403 setmode(put_fd
, O_BINARY
);
406 if (fstat(put_fd
, &sb
) < 0) {
408 std::cerr
<< "ERROR: can't identify length of file (" << xstrerr(xerrno
) << ")" << std::endl
;
413 char *newhost
= strstr(url
, "://");
417 newhost
= xstrdup(newhost
);
418 t
= newhost
+ strcspn(newhost
, "@/?");
421 t
= newhost
+ strcspn(newhost
, "@/?");
428 if (version
[0] == '-' || !version
[0]) {
429 /* HTTP/0.9, no headers, no version */
430 snprintf(msg
, BUFSIZ
, "%s %s\r\n", method
, url
);
432 if (!xisdigit(version
[0])) // not HTTP/n.n
433 snprintf(msg
, BUFSIZ
, "%s %s %s\r\n", method
, url
, version
);
435 snprintf(msg
, BUFSIZ
, "%s %s HTTP/%s\r\n", method
, url
, version
);
438 snprintf(buf
, BUFSIZ
, "Host: %s\r\n", host
);
442 if (useragent
== NULL
) {
443 snprintf(buf
, BUFSIZ
, "User-Agent: squidclient/%s\r\n", VERSION
);
445 } else if (useragent
[0] != '\0') {
446 snprintf(buf
, BUFSIZ
, "User-Agent: %s\r\n", useragent
);
451 snprintf(buf
, BUFSIZ
, "Cache-Control: no-cache\r\n");
455 snprintf(buf
, BUFSIZ
, "Content-length: %" PRId64
"\r\n", (int64_t) sb
.st_size
);
458 if (opt_noaccept
== 0) {
459 snprintf(buf
, BUFSIZ
, "Accept: */*\r\n");
463 snprintf(buf
, BUFSIZ
, "If-Modified-Since: %s\r\n", mkrfc1123(ims
));
466 if (max_forwards
> -1) {
467 snprintf(buf
, BUFSIZ
, "Max-Forwards: %d\r\n", max_forwards
);
470 struct base64_encode_ctx ctx
;
471 base64_encode_init(&ctx
);
474 const char *user
= proxy_user
;
475 const char *password
= proxy_password
;
478 password
= getpass("Proxy password: ");
481 std::cerr
<< "ERROR: Proxy password missing" << std::endl
;
484 uint8_t *pwdBuf
= new uint8_t[base64_encode_len(strlen(user
)+1+strlen(password
))];
485 blen
= base64_encode_update(&ctx
, pwdBuf
, strlen(user
), reinterpret_cast<const uint8_t*>(user
));
486 blen
+= base64_encode_update(&ctx
, pwdBuf
+blen
, 1, reinterpret_cast<const uint8_t*>(":"));
487 blen
+= base64_encode_update(&ctx
, pwdBuf
+blen
, strlen(password
), reinterpret_cast<const uint8_t*>(password
));
488 blen
+= base64_encode_final(&ctx
, pwdBuf
+blen
);
489 snprintf(buf
, BUFSIZ
, "Proxy-Authorization: Basic %.*s\r\n", (int)blen
, reinterpret_cast<char*>(pwdBuf
));
494 const char *user
= www_user
;
495 const char *password
= www_password
;
498 password
= getpass("WWW password: ");
501 std::cerr
<< "ERROR: WWW password missing" << std::endl
;
504 uint8_t *pwdBuf
= new uint8_t[base64_encode_len(strlen(user
)+1+strlen(password
))];
505 blen
= base64_encode_update(&ctx
, pwdBuf
, strlen(user
), reinterpret_cast<const uint8_t*>(user
));
506 blen
+= base64_encode_update(&ctx
, pwdBuf
+blen
, 1, reinterpret_cast<const uint8_t*>(":"));
507 blen
+= base64_encode_update(&ctx
, pwdBuf
+blen
, strlen(password
), reinterpret_cast<const uint8_t*>(password
));
508 blen
+= base64_encode_final(&ctx
, pwdBuf
+blen
);
509 snprintf(buf
, BUFSIZ
, "Authorization: Basic %.*s\r\n", (int)blen
, reinterpret_cast<char*>(pwdBuf
));
516 const char *token
= GSSAPI_token(host
);
517 snprintf(buf
, BUFSIZ
, "Authorization: Negotiate %s\r\n", token
);
521 std::cerr
<< "ERROR: server host missing" << std::endl
;
524 if (Transport::Config
.hostname
) {
525 const char *token
= GSSAPI_token(Transport::Config
.hostname
);
526 snprintf(buf
, BUFSIZ
, "Proxy-Authorization: Negotiate %s\r\n", token
);
530 std::cerr
<< "ERROR: proxy server host missing" << std::endl
;
534 /* HTTP/1.0 may need keep-alive explicitly */
535 if (strcmp(version
, "1.0") == 0 && keep_alive
)
536 strcat(msg
, "Connection: keep-alive\r\n");
538 /* HTTP/1.1 may need close explicitly */
540 strcat(msg
, "Connection: close\r\n");
542 strcat(msg
, extra_hdrs
);
546 debugVerbose(1, "Request:" << std::endl
<< msg
<< std::endl
<< ".");
548 uint32_t loops
= Ping::Init();
550 for (uint32_t i
= 0; loops
== 0 || i
< loops
; ++i
) {
553 if (!Transport::Connect())
556 /* Send the HTTP request */
557 debugVerbose(2, "Sending HTTP request ... ");
558 bytesWritten
= Transport::Write(msg
, strlen(msg
));
560 if (bytesWritten
< 0) {
561 std::cerr
<< "ERROR: write" << std::endl
;
563 } else if ((unsigned) bytesWritten
!= strlen(msg
)) {
564 std::cerr
<< "ERROR: Cannot send request?: " << std::endl
<< msg
<< std::endl
;
567 debugVerbose(2, "done.");
570 debugVerbose(1, "Sending HTTP request payload ...");
572 lseek(put_fd
, 0, SEEK_SET
);
573 while ((x
= read(put_fd
, buf
, sizeof(buf
))) > 0) {
575 x
= Transport::Write(buf
, x
);
584 std::cerr
<< "ERROR: Cannot send file." << std::endl
;
586 debugVerbose(1, "done.");
591 setmode(1, O_BINARY
);
594 while ((len
= Transport::Read(buf
, sizeof(buf
))) > 0) {
597 if (to_stdout
&& fwrite(buf
, len
, 1, stdout
) != 1) {
599 std::cerr
<< "ERROR: writing to stdout: " << xstrerr(xerrno
) << std::endl
;
604 if (Transport::Config
.tlsEnabled
) {
606 std::cerr
<< "- Peer has closed the TLS connection" << std::endl
;
607 } else if (!gnutls_error_is_fatal(len
)) {
608 std::cerr
<< "WARNING: " << gnutls_strerror(len
) << std::endl
;
610 std::cerr
<< "ERROR: " << gnutls_strerror(len
) << std::endl
;
619 Transport::CloseConnection();
621 if (Ping::LoopDone(i
))
624 Ping::TimerStop(fsize
);
627 Ping::DisplayStats();
628 Transport::ShutdownTls();
635 std::cerr
<< "SIGPIPE received." << std::endl
;
643 sa
.sa_handler
= pipe_handler
;
644 sa
.sa_flags
= SA_RESTART
;
645 sigemptyset(&sa
.sa_mask
);
647 if (sigaction(SIGPIPE
, &sa
, NULL
) < 0) {
648 std::cerr
<< "ERROR: Cannot set PIPE signal." << std::endl
;
652 signal(SIGPIPE
, pipe_handler
);