]> git.ipfire.org Git - thirdparty/squid.git/blame - tools/squidclient/squidclient.cc
Maintenance: Removed most NULLs using modernize-use-nullptr (#1075)
[thirdparty/squid.git] / tools / squidclient / squidclient.cc
CommitLineData
30a4f2a8 1/*
bf95c10a 2 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
e25c139f 3 *
5f623035
AJ
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.
30a4f2a8 7 */
090089c4 8
f7f3304a 9#include "squid.h"
25f98340 10#include "base64.h"
055421ee 11#include "ip/Address.h"
0ced8e5d 12#include "ip/tools.h"
98cacedb 13#include "time/gadgets.h"
b2050d4d 14#include "tools/squidclient/gssapi_support.h"
842cd45a
AJ
15#include "tools/squidclient/Parameters.h"
16#include "tools/squidclient/Ping.h"
b4f805f6 17#include "tools/squidclient/Transport.h"
94ab55b0 18
0e25b470 19#if _SQUID_WINDOWS_
63be0a78 20/** \cond AUTODOCS-IGNORE */
15443eec 21using namespace Squid;
63be0a78 22/** \endcond */
15443eec 23#endif
24
3f9f5878 25#include <cassert>
074d6a40 26#include <cerrno>
b38767c7 27#include <csignal>
074d6a40 28#include <cstring>
cf6e492b 29#include <iostream>
4b19fa9e 30#include <sstream>
be266cb2 31#if _SQUID_WINDOWS_
b55fa77d 32#include <io.h>
33#endif
815f9118 34#if HAVE_SYS_SOCKET_H
94ab55b0 35#include <sys/socket.h>
815f9118 36#endif
815f9118 37#if HAVE_UNISTD_H
94ab55b0 38#include <unistd.h>
815f9118 39#endif
489520a9 40#if HAVE_NETDB_H
94ab55b0 41#include <netdb.h>
815f9118 42#endif
815f9118 43#if HAVE_SYS_STAT_H
94ab55b0 44#include <sys/stat.h>
815f9118 45#endif
46#if HAVE_FCNTL_H
94ab55b0 47#include <fcntl.h>
815f9118 48#endif
49#if HAVE_NETINET_IN_H
c7f83c7a 50#include <netinet/in.h>
815f9118 51#endif
d3e3ff4f 52#if HAVE_GETOPT_H
53#include <getopt.h>
54#endif
94ab55b0 55
090089c4 56#ifndef BUFSIZ
f53969cc 57#define BUFSIZ 8192
7ed62376 58#endif
090089c4 59
60/* Local functions */
f5b8bbc4 61static void usage(const char *progname);
62e76326 62
b38767c7 63void pipe_handler(int sig);
d6d09e02 64static void set_our_signal(void);
823d23e4 65
842cd45a
AJ
66Parameters scParams;
67
cca89eeb 68static int put_fd;
aee3523a 69static char *put_file = nullptr;
62e76326 70
b6c6bcef 71static struct stat sb;
72int total_bytes = 0;
090089c4 73
3dc72a57
AJ
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 */
78fde *fde::Table = NULL;
79#endif
80
0e25b470 81#if _SQUID_WINDOWS_
ec556193
GS
82void
83Win32SockCleanup(void)
84{
85 WSACleanup();
86 return;
87}
1191b93b 88#endif
ec556193 89
b8d8561b 90static void
0ee4272b 91usage(const char *progname)
090089c4 92{
cf6e492b 93 std::cerr << "Version: " << VERSION << std::endl
842cd45a 94 << "Usage: " << progname << " [Basic Options] [HTTP Options]" << std::endl
b4f805f6
AJ
95 << std::endl;
96 std::cerr
f53969cc
SM
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;
b4f805f6 104 Transport::Config.usage();
842cd45a
AJ
105 Ping::Config.usage();
106 std::cerr
f53969cc
SM
107 << "HTTP Options:" << std::endl
108 << " -a Do NOT include Accept: header." << std::endl
109 << " -A User-Agent: header. Use \"\" to omit." << std::endl
8798e209 110 << " -H 'string' Extra headers to send. Supports '\\\\', '\\n', '\\r' and '\\t'." << std::endl
f53969cc
SM
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
823d23e4 115#if HAVE_GSSAPI
f53969cc
SM
116 << " -n Proxy Negotiate(Kerberos) authentication" << std::endl
117 << " -N WWW Negotiate(Kerberos) authentication" << std::endl
842cd45a 118#endif
f53969cc
SM
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 ;
24885773 128 exit(EXIT_FAILURE);
090089c4 129}
130
8798e209
AJ
131static void
132shellUnescape(char *buf)
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
3f9f5878
AR
181/// [Proxy-]Authorization header producer
182class Authorization
183{
184public:
185 Authorization(const char *aHeader, const char *aDestination):
186 header(aHeader), destination(aDestination) {}
187
188 /// finalizes and writes the right HTTP header to the given stream
189 void commit(std::ostream &os);
190
191 std::string header; ///< HTTP header name to send
192 std::string destination; ///< used when describing password
193 const char *user = nullptr; ///< user name to encode and send
194 const char *password = nullptr; ///< user password to encode and send
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
b8d8561b 231int
232main(int argc, char *argv[])
090089c4 233{
b4f805f6 234 int len, bytesWritten;
2f399d29 235 bool to_stdout, reload;
599eadbe 236 int keep_alive = 0;
88738790 237 int opt_noaccept = 0;
14b85b48
DK
238#if HAVE_GSSAPI
239 int www_neg = 0, proxy_neg = 0;
240#endif
4b19fa9e
AJ
241 char url[BUFSIZ];
242 char buf[BUFSIZ];
243 char *extra_hdrs = nullptr;
0ee4272b 244 const char *method = "GET";
090089c4 245 extern char *optarg;
234967c9 246 time_t ims = 0;
b3b64e58 247 int max_forwards = -1;
62e76326 248
aee3523a 249 const char *host = nullptr;
5ac5029d 250 const char *version = "1.0";
aee3523a 251 const char *useragent = nullptr;
090089c4 252
253 /* set the defaults */
2f399d29
AJ
254 to_stdout = true;
255 reload = false;
090089c4 256
0ced8e5d 257 Ip::ProbeTransport(); // determine IPv4 or IPv6 capabilities before parsing.
dc9d555d 258 if (argc < 2 || argv[argc-1][0] == '-') {
f53969cc 259 usage(argv[0]); /* need URL */
090089c4 260 } else if (argc >= 2) {
4b19fa9e
AJ
261 strncpy(url, argv[argc - 1], sizeof(url));
262 url[sizeof(url) - 1] = '\0';
62e76326 263
dc9d555d 264 int optIndex = 0;
d8c201b7 265 const char *shortOpStr = "aA:h:j:V:l:P:i:km:nNp:rsvt:H:T:u:U:w:W:?";
dc9d555d
AJ
266
267 // options for controlling squidclient
842cd45a
AJ
268 static struct option basicOptions[] = {
269 /* These are the generic options for squidclient itself */
aee3523a
AR
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}
dc9d555d
AJ
279 };
280
281 int c;
282 while ((c = getopt_long(argc, argv, shortOpStr, basicOptions, &optIndex)) != -1) {
842cd45a
AJ
283
284 // modules parse their own specific options
3afd7aae 285 switch (c) {
842cd45a
AJ
286 case '\1':
287 to_stdout = 0;
b4f805f6
AJ
288 Ping::Config.parseCommandOpts(argc, argv, c, optIndex);
289 continue;
290
f53969cc
SM
291 case 'h': /* remote host */
292 case 'l': /* local host */
293 case 'p': /* port number */
b4f805f6
AJ
294 // rewind and let the Transport::Config parser handle
295 optind -= 2;
8b082ed9
FC
296 Transport::Config.parseCommandOpts(argc, argv, c, optIndex);
297 continue;
b4f805f6
AJ
298
299 case '\3': // request over a TLS connection
300 Transport::Config.parseCommandOpts(argc, argv, c, optIndex);
301 continue;
842cd45a
AJ
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;
62e76326 311
3afd7aae
AJ
312 case 'a':
313 opt_noaccept = 1;
314 break;
62e76326 315
ba7ce724 316 case 'A':
be05c185 317 useragent = optarg;
ba7ce724
AJ
318 break;
319
5ac5029d 320 case 'j':
af6a12ee
AJ
321 host = optarg;
322 break;
5ac5029d
AJ
323
324 case 'V':
be05c185 325 version = optarg;
3afd7aae 326 break;
62e76326 327
f53969cc 328 case 's': /* silent */
2f399d29 329 to_stdout = false;
3afd7aae 330 break;
62e76326 331
f53969cc 332 case 'k': /* backward compat */
3afd7aae 333 keep_alive = 1;
3afd7aae 334 break;
62e76326 335
f53969cc 336 case 'r': /* reload */
2f399d29 337 reload = true;
3afd7aae 338 break;
62e76326 339
3afd7aae
AJ
340 case 'P':
341 put_file = xstrdup(optarg);
3afd7aae 342 break;
62e76326 343
f53969cc 344 case 'i': /* IMS */
3afd7aae 345 ims = (time_t) atoi(optarg);
3afd7aae 346 break;
62e76326 347
3afd7aae
AJ
348 case 'm':
349 method = xstrdup(optarg);
3afd7aae 350 break;
62e76326 351
3afd7aae
AJ
352 case 't':
353 method = xstrdup("TRACE");
3afd7aae 354 max_forwards = atoi(optarg);
3afd7aae 355 break;
62e76326 356
3afd7aae
AJ
357 case 'H':
358 if (strlen(optarg)) {
4b19fa9e
AJ
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);
8798e209 364 shellUnescape(extra_hdrs);
3afd7aae
AJ
365 }
366 break;
62e76326 367
3afd7aae 368 case 'T':
b4f805f6 369 Transport::Config.ioTimeout = atoi(optarg);
3afd7aae 370 break;
62e76326 371
3afd7aae 372 case 'u':
3f9f5878 373 ProxyAuthorization.user = optarg;
3afd7aae 374 break;
62e76326 375
3afd7aae 376 case 'w':
3f9f5878 377 ProxyAuthorization.password = optarg;
3afd7aae 378 break;
62e76326 379
3afd7aae 380 case 'U':
3f9f5878 381 OriginAuthorization.user = optarg;
3afd7aae 382 break;
62e76326 383
3afd7aae 384 case 'W':
3f9f5878 385 OriginAuthorization.password = optarg;
3afd7aae 386 break;
62e76326 387
823d23e4 388 case 'n':
dc9d555d 389#if HAVE_GSSAPI
823d23e4 390 proxy_neg = 1;
dc9d555d
AJ
391#else
392 std::cerr << "ERROR: Negotiate authentication not supported." << std::endl;
393 usage(argv[0]);
394#endif
823d23e4
AJ
395 break;
396
397 case 'N':
dc9d555d 398#if HAVE_GSSAPI
823d23e4 399 www_neg = 1;
dc9d555d
AJ
400#else
401 std::cerr << "ERROR: Negotiate authentication not supported." << std::endl;
402 usage(argv[0]);
823d23e4 403#endif
dc9d555d
AJ
404 break;
405
3afd7aae
AJ
406 case 'v':
407 /* undocumented: may increase verb-level by giving more -v's */
842cd45a
AJ
408 ++scParams.verbosityLevel;
409 debugVerbose(2, "verbosity level set to " << scParams.verbosityLevel);
3afd7aae 410 break;
62e76326 411
f53969cc 412 case '?': /* usage */
62e76326 413
3afd7aae
AJ
414 default:
415 usage(argv[0]);
416 break;
417 }
dc9d555d 418 }
090089c4 419 }
0e25b470 420#if _SQUID_WINDOWS_
0ef0f1de 421 {
3afd7aae
AJ
422 WSADATA wsaData;
423 WSAStartup(2, &wsaData);
424 atexit(Win32SockCleanup);
0ef0f1de 425 }
426#endif
090089c4 427 /* Build the HTTP request */
8a9b6b94 428 if (strncmp(url, "mgr:", 4) == 0) {
3afd7aae 429 char *t = xstrdup(url + 4);
aee3523a 430 const char *at = nullptr;
3f3e5473 431 if (!strrchr(t, '@')) { // ignore any -w password if @ is explicit already.
3f9f5878 432 at = ProxyAuthorization.password;
3f3e5473
AJ
433 }
434 // embed the -w proxy password into old-style cachemgr URLs
435 if (at)
4b19fa9e 436 snprintf(url, sizeof(url), "cache_object://%s/%s@%s", Transport::Config.hostname, t, at);
3f3e5473 437 else
4b19fa9e 438 snprintf(url, sizeof(url), "cache_object://%s/%s", Transport::Config.hostname, t);
3afd7aae 439 xfree(t);
8a9b6b94 440 }
cca89eeb 441 if (put_file) {
3afd7aae
AJ
442 put_fd = open(put_file, O_RDONLY);
443 set_our_signal();
444
445 if (put_fd < 0) {
b69e9ffa
AJ
446 int xerrno = errno;
447 std::cerr << "ERROR: can't open file (" << xstrerr(xerrno) << ")" << std::endl;
24885773 448 exit(EXIT_FAILURE);
3afd7aae 449 }
be266cb2 450#if _SQUID_WINDOWS_
3afd7aae 451 setmode(put_fd, O_BINARY);
c4aefe96 452#endif
62e76326 453
be05c185 454 if (fstat(put_fd, &sb) < 0) {
b69e9ffa
AJ
455 int xerrno = errno;
456 std::cerr << "ERROR: can't identify length of file (" << xstrerr(xerrno) << ")" << std::endl;
be05c185 457 }
cca89eeb 458 }
5ac5029d
AJ
459
460 if (!host) {
af6a12ee
AJ
461 char *newhost = strstr(url, "://");
462 if (newhost) {
463 char *t;
464 newhost += 3;
dc47f531 465 newhost = xstrdup(newhost);
af6a12ee
AJ
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 }
5ac5029d
AJ
474 }
475
4b19fa9e
AJ
476 std::stringstream msg;
477
8d55d7ef 478 if (version[0] == '-' || !version[0]) {
af6a12ee 479 /* HTTP/0.9, no headers, no version */
4b19fa9e 480 msg << method << " " << url << "\r\n";
5ac5029d 481 } else {
4b19fa9e
AJ
482 const auto versionImpliesHttp = xisdigit(version[0]); // is HTTP/n.n
483 msg << method << " "
484 << url << " "
485 << (versionImpliesHttp ? "HTTP/" : "") << version
486 << "\r\n";
62e76326 487
af6a12ee 488 if (host) {
4b19fa9e 489 msg << "Host: " << host << "\r\n";
af6a12ee
AJ
490 }
491
4b19fa9e
AJ
492 if (!useragent) {
493 msg << "User-Agent: squidclient/" << VERSION << "\r\n";
ba7ce724 494 } else if (useragent[0] != '\0') {
4b19fa9e
AJ
495 msg << "User-Agent: " << useragent << "\r\n";
496 } // else custom: no value U-A header
ba7ce724 497
af6a12ee 498 if (reload) {
4b19fa9e 499 msg << "Cache-Control: no-cache\r\n";
af6a12ee
AJ
500 }
501 if (put_fd > 0) {
4b19fa9e 502 msg << "Content-length: " << sb.st_size << "\r\n";
af6a12ee
AJ
503 }
504 if (opt_noaccept == 0) {
4b19fa9e 505 msg << "Accept: */*\r\n";
af6a12ee
AJ
506 }
507 if (ims) {
98cacedb 508 msg << "If-Modified-Since: " << Time::FormatRfc1123(ims) << "\r\n";
af6a12ee
AJ
509 }
510 if (max_forwards > -1) {
4b19fa9e 511 msg << "Max-Forwards: " << max_forwards << "\r\n";
af6a12ee 512 }
3f9f5878
AR
513 if (ProxyAuthorization.user)
514 ProxyAuthorization.commit(msg);
515 if (OriginAuthorization.user)
516 OriginAuthorization.commit(msg);
823d23e4
AJ
517#if HAVE_GSSAPI
518 if (www_neg) {
519 if (host) {
aadbbd7d 520 const char *token = GSSAPI_token(host);
4b19fa9e 521 msg << "Proxy-Authorization: Negotiate " << token << "\r\n";
b745a16e 522 delete[] token;
823d23e4 523 } else
95c25f66 524 std::cerr << "ERROR: server host missing" << std::endl;
823d23e4
AJ
525 }
526 if (proxy_neg) {
b4f805f6 527 if (Transport::Config.hostname) {
aadbbd7d 528 const char *token = GSSAPI_token(Transport::Config.hostname);
4b19fa9e 529 msg << "Proxy-Authorization: Negotiate " << token << "\r\n";
b745a16e 530 delete[] token;
823d23e4 531 } else
95c25f66 532 std::cerr << "ERROR: proxy server host missing" << std::endl;
823d23e4
AJ
533 }
534#endif
af6a12ee 535
95e78500
AJ
536 /* HTTP/1.0 may need keep-alive explicitly */
537 if (strcmp(version, "1.0") == 0 && keep_alive)
4b19fa9e 538 msg << "Connection: keep-alive\r\n";
95e78500
AJ
539
540 /* HTTP/1.1 may need close explicitly */
51d94d10 541 if (!keep_alive)
4b19fa9e 542 msg << "Connection: close\r\n";
af6a12ee 543
4b19fa9e
AJ
544 if (extra_hdrs) {
545 msg << extra_hdrs;
546 safe_free(extra_hdrs);
547 }
548 msg << "\r\n"; // empty line ends MIME header block
a78886fc 549 }
5ac5029d 550
4b19fa9e
AJ
551 msg.flush();
552 const auto messageHeader = msg.str();
553 debugVerbose(1, "Request:" << std::endl << messageHeader << std::endl << ".");
63259c34 554
842cd45a 555 uint32_t loops = Ping::Init();
62e76326 556
b38767c7 557 for (uint32_t i = 0; loops == 0 || i < loops; ++i) {
842cd45a 558 size_t fsize = 0;
cc192b50 559
b4f805f6
AJ
560 if (!Transport::Connect())
561 continue;
988e90e1 562
3afd7aae 563 /* Send the HTTP request */
95c25f66 564 debugVerbose(2, "Sending HTTP request ... ");
4b19fa9e 565 bytesWritten = Transport::Write(messageHeader.data(), messageHeader.length());
988e90e1 566
3afd7aae 567 if (bytesWritten < 0) {
95c25f66 568 std::cerr << "ERROR: write" << std::endl;
24885773 569 exit(EXIT_FAILURE);
4b19fa9e
AJ
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;
24885773 573 exit(EXIT_FAILURE);
3afd7aae 574 }
95c25f66 575 debugVerbose(2, "done.");
cc192b50 576
3afd7aae 577 if (put_file) {
95c25f66 578 debugVerbose(1, "Sending HTTP request payload ...");
3afd7aae 579 int x;
cf4ae693
AJ
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) {
62e76326 585
d64ed7f5 586 x = Transport::Write(buf, x);
62e76326 587
d64ed7f5 588 total_bytes += x;
62e76326 589
d64ed7f5
SM
590 if (x <= 0)
591 break;
592 }
62e76326 593
3afd7aae 594 if (x != 0)
95c25f66 595 std::cerr << "ERROR: Cannot send file." << std::endl;
b65ab6e8 596 else
95c25f66 597 debugVerbose(1, "done.");
3afd7aae
AJ
598 }
599 /* Read the data */
54220df8 600
0e25b470 601#if _SQUID_WINDOWS_
3afd7aae 602 setmode(1, O_BINARY);
00f768c1 603#endif
62e76326 604
b4f805f6 605 while ((len = Transport::Read(buf, sizeof(buf))) > 0) {
3afd7aae 606 fsize += len;
62e76326 607
b69e9ffa
AJ
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 }
3afd7aae 612 }
62e76326 613
b4f805f6
AJ
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
0e25b470 626#if _SQUID_WINDOWS_
3afd7aae 627 setmode(1, O_TEXT);
0ef0f1de 628#endif
62e76326 629
b4f805f6 630 Transport::CloseConnection();
62e76326 631
842cd45a 632 if (Ping::LoopDone(i))
3afd7aae 633 break;
62e76326 634
842cd45a 635 Ping::TimerStop(fsize);
090089c4 636 }
899bab3f 637
842cd45a 638 Ping::DisplayStats();
b4f805f6 639 Transport::ShutdownTls();
24885773 640 return EXIT_SUCCESS;
090089c4 641}
642
daacd51f 643void
ced8def3 644pipe_handler(int)
e1381638 645{
bf989d30 646 std::cerr << "SIGPIPE received." << std::endl;
54220df8 647}
648
649static void
e1381638
AJ
650set_our_signal(void)
651{
54220df8 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);
62e76326 657
aee3523a 658 if (sigaction(SIGPIPE, &sa, nullptr) < 0) {
bf989d30 659 std::cerr << "ERROR: Cannot set PIPE signal." << std::endl;
24885773 660 exit(EXIT_FAILURE);
54220df8 661 }
662#else
663 signal(SIGPIPE, pipe_handler);
664#endif
54220df8 665}
f53969cc 666