]> git.ipfire.org Git - thirdparty/squid.git/blame - tools/squidclient.cc
Clean up use of USE_POLL/HAVE_POLL
[thirdparty/squid.git] / tools / squidclient.cc
CommitLineData
16300b58 1
30a4f2a8 2/*
bad9016a 3 * $Id: squidclient.cc,v 1.8 2007/08/10 09:07:01 amosjeffries Exp $
30a4f2a8 4 *
5 * DEBUG: section 0 WWW Client
6 * AUTHOR: Harvest Derived
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
30a4f2a8 10 *
2b6662ba 11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
30a4f2a8 19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
30a4f2a8 34 */
090089c4 35
94ab55b0 36#include "config.h"
37
15443eec 38#ifdef _SQUID_MSWIN_
39using namespace Squid;
40#endif
41
b55fa77d 42#ifdef _SQUID_WIN32_
43#include <io.h>
44#endif
815f9118 45#if HAVE_STDIO_H
94ab55b0 46#include <stdio.h>
815f9118 47#endif
48#if HAVE_STDLIB_H
94ab55b0 49#include <stdlib.h>
815f9118 50#endif
51#if HAVE_SYS_TYPES_H
94ab55b0 52#include <sys/types.h>
815f9118 53#endif
54#if HAVE_SYS_SOCKET_H
94ab55b0 55#include <sys/socket.h>
815f9118 56#endif
57#if HAVE_STRING_H
94ab55b0 58#include <string.h>
815f9118 59#endif
60#if HAVE_UNISTD_H
94ab55b0 61#include <unistd.h>
815f9118 62#endif
63#if HAVE_NETDB_H && !defined(_SQUID_NETDB_H_) /* protect NEXTSTEP */
64#define _SQUID_NETDB_H_
94ab55b0 65#include <netdb.h>
815f9118 66#endif
67#if HAVE_SIGNAL_H
94ab55b0 68#include <signal.h>
815f9118 69#endif
70#if HAVE_ERRNO_H
94ab55b0 71#include <errno.h>
815f9118 72#endif
73#if HAVE_SYS_STAT_H
94ab55b0 74#include <sys/stat.h>
815f9118 75#endif
76#if HAVE_FCNTL_H
94ab55b0 77#include <fcntl.h>
815f9118 78#endif
79#if HAVE_NETINET_IN_H
c7f83c7a 80#include <netinet/in.h>
815f9118 81#endif
d3e3ff4f 82#if HAVE_GETOPT_H
83#include <getopt.h>
84#endif
94ab55b0 85
86#include "util.h"
090089c4 87
88#ifndef BUFSIZ
89#define BUFSIZ 8192
90#endif
91
94ab55b0 92typedef void SIGHDLR(int sig);
93
090089c4 94/* Local functions */
a2c963ae 95static int client_comm_bind(int, const char *);
62e76326 96
a2c963ae 97static int client_comm_connect(int, const char *, u_short, struct timeval *);
f5b8bbc4 98static void usage(const char *progname);
62e76326 99
899bab3f 100static int Now(struct timeval *);
e6ccf245 101static SIGHDLR catchSignal;
54220df8 102static SIGHDLR pipe_handler;
d6d09e02 103static void set_our_signal(void);
20cbfe5a 104static ssize_t myread(int fd, void *buf, size_t len);
105static ssize_t mywrite(int fd, void *buf, size_t len);
cca89eeb 106static int put_fd;
107static char *put_file = NULL;
62e76326 108
b6c6bcef 109static struct stat sb;
110int total_bytes = 0;
20cbfe5a 111int io_timeout = 120;
090089c4 112
b8d8561b 113static void
0ee4272b 114usage(const char *progname)
090089c4 115{
0ee4272b 116 fprintf(stderr,
bad9016a 117 "Version: %s\n"
62e76326 118 "Usage: %s [-arsv] [-i IMS] [-h remote host] [-l local host] [-p port] [-m method] [-t count] [-I ping-interval] [-H 'strings'] [-T timeout] url\n"
119 "Options:\n"
120 " -P file PUT request.\n"
121 " -a Do NOT include Accept: header.\n"
122 " -r Force cache to reload URL.\n"
123 " -s Silent. Do not print data to stdout.\n"
124 " -v Verbose. Print outgoing message to stderr.\n"
125 " -i IMS If-Modified-Since time (in Epoch seconds).\n"
126 " -h host Retrieve URL from cache on hostname. Default is localhost.\n"
127 " -l host Specify a local IP address to bind to. Default is none.\n"
128 " -p port Port number of cache. Default is %d.\n"
129 " -m method Request method, default is GET.\n"
130 " -t count Trace count cache-hops\n"
131 " -g count Ping mode, \"count\" iterations (0 to loop until interrupted).\n"
132 " -I interval Ping interval in seconds (default 1 second).\n"
133 " -H 'string' Extra headers to send. Use '\\n' for new lines.\n"
134 " -T timeout Timeout value (seconds) for read/write operations.\n"
135 " -u user Proxy authentication username\n"
136 " -w password Proxy authentication password\n"
137 " -U user WWW authentication username\n"
138 " -W password WWW authentication password\n",
bad9016a 139 VERSION, progname, CACHE_HTTP_PORT);
090089c4 140 exit(1);
141}
142
899bab3f 143static int interrupted = 0;
b8d8561b 144int
145main(int argc, char *argv[])
090089c4 146{
147 int conn, c, len, bytesWritten;
148 int port, to_stdout, reload;
899bab3f 149 int ping, pcount;
599eadbe 150 int keep_alive = 0;
88738790 151 int opt_noaccept = 0;
63259c34 152 int opt_verbose = 0;
a2c963ae 153 const char *hostname, *localhost;
f8230a8d 154 char url[BUFSIZ], msg[49152], buf[BUFSIZ];
155 char extra_hdrs[32768];
0ee4272b 156 const char *method = "GET";
090089c4 157 extern char *optarg;
234967c9 158 time_t ims = 0;
b3b64e58 159 int max_forwards = -1;
62e76326 160
899bab3f 161 struct timeval tv1, tv2;
162 int i = 0, loops;
163 long ping_int;
164 long ping_min = 0, ping_max = 0, ping_sum = 0, ping_mean = 0;
a78886fc 165 char *proxy_user = NULL;
166 char *proxy_password = NULL;
167 char *www_user = NULL;
168 char *www_password = NULL;
090089c4 169
170 /* set the defaults */
2c08acd9 171 hostname = "localhost";
172 localhost = NULL;
63259c34 173 extra_hdrs[0] = '\0';
090089c4 174 port = CACHE_HTTP_PORT;
175 to_stdout = 1;
176 reload = 0;
899bab3f 177 ping = 0;
178 pcount = 0;
179 ping_int = 1 * 1000;
090089c4 180
181 if (argc < 2) {
62e76326 182 usage(argv[0]); /* need URL */
090089c4 183 } else if (argc >= 2) {
62e76326 184 strncpy(url, argv[argc - 1], BUFSIZ);
185 url[BUFSIZ - 1] = '\0';
186
187 if (url[0] == '-')
188 usage(argv[0]);
189
190 while ((c = getopt(argc, argv, "ah:l:P:i:km:p:rsvt:g:p:I:H:T:u:U:w:W:?")) != -1)
191 switch (c) {
192
193 case 'a':
194 opt_noaccept = 1;
195 break;
196
197 case 'h': /* remote host */
198
199 if (optarg != NULL)
200 hostname = optarg;
201
202 break;
203
204 case 'l': /* local host */
205 if (optarg != NULL)
206 localhost = optarg;
207
208 break;
209
210 case 's': /* silent */
211 to_stdout = 0;
212
213 break;
214
215 case 'k': /* backward compat */
216 keep_alive = 1;
217
218 break;
219
220 case 'r': /* reload */
221 reload = 1;
222
223 break;
224
225 case 'p': /* port number */
226 sscanf(optarg, "%d", &port);
227
228 if (port < 1)
229 port = CACHE_HTTP_PORT; /* default */
230
231 break;
232
233 case 'P':
234 put_file = xstrdup(optarg);
235
236 break;
237
238 case 'i': /* IMS */
239 ims = (time_t) atoi(optarg);
240
241 break;
242
243 case 'm':
244 method = xstrdup(optarg);
245
246 break;
247
248 case 't':
249 method = xstrdup("TRACE");
250
251 max_forwards = atoi(optarg);
252
253 break;
254
255 case 'g':
256 ping = 1;
257
258 pcount = atoi(optarg);
259
260 to_stdout = 0;
261
262 break;
263
264 case 'I':
265 if ((ping_int = atoi(optarg) * 1000) <= 0)
266 usage(argv[0]);
267
268 break;
269
270 case 'H':
271 if (strlen(optarg)) {
272 char *t;
273 strncpy(extra_hdrs, optarg, sizeof(extra_hdrs));
274
275 while ((t = strstr(extra_hdrs, "\\n")))
276 *t = '\r', *(t + 1) = '\n';
277 }
278
279 break;
280
281 case 'T':
282 io_timeout = atoi(optarg);
283 break;
284
285 case 'u':
286 proxy_user = optarg;
287 break;
288
289 case 'w':
290 proxy_password = optarg;
291 break;
292
293 case 'U':
294 www_user = optarg;
295 break;
296
297 case 'W':
298 www_password = optarg;
299 break;
300
301 case 'v':
302 /* undocumented: may increase verb-level by giving more -v's */
303 opt_verbose++;
304 break;
305
306 case '?': /* usage */
307
308 default:
309 usage(argv[0]);
310 break;
311 }
090089c4 312 }
62e76326 313
0ef0f1de 314#ifdef _SQUID_MSWIN_
315 {
62e76326 316 WSADATA wsaData;
317 WSAStartup(2, &wsaData);
0ef0f1de 318 }
319#endif
090089c4 320 /* Build the HTTP request */
8a9b6b94 321 if (strncmp(url, "mgr:", 4) == 0) {
62e76326 322 char *t = xstrdup(url + 4);
323 snprintf(url, BUFSIZ, "cache_object://%s/%s", hostname, t);
324 xfree(t);
8a9b6b94 325 }
62e76326 326
cca89eeb 327 if (put_file) {
62e76326 328 put_fd = open(put_file, O_RDONLY);
329 set_our_signal();
330
331 if (put_fd < 0) {
332 fprintf(stderr, "%s: can't open file (%s)\n", argv[0],
333 xstrerror());
334 exit(-1);
335 }
336
ec4daaa5 337#ifdef _SQUID_WIN32_
62e76326 338 setmode(put_fd, O_BINARY);
339
c4aefe96 340#endif
62e76326 341
342 fstat(put_fd, &sb);
cca89eeb 343 }
62e76326 344
042461c3 345 snprintf(msg, BUFSIZ, "%s %s HTTP/1.0\r\n", method, url);
62e76326 346
090089c4 347 if (reload) {
62e76326 348 snprintf(buf, BUFSIZ, "Pragma: no-cache\r\n");
349 strcat(msg, buf);
234967c9 350 }
62e76326 351
16300b58 352 if (put_fd > 0) {
62e76326 353 snprintf(buf, BUFSIZ, "Content-length: %d\r\n", (int) sb.st_size);
354 strcat(msg, buf);
cca89eeb 355 }
62e76326 356
88738790 357 if (opt_noaccept == 0) {
62e76326 358 snprintf(buf, BUFSIZ, "Accept: */*\r\n");
359 strcat(msg, buf);
88738790 360 }
62e76326 361
234967c9 362 if (ims) {
62e76326 363 snprintf(buf, BUFSIZ, "If-Modified-Since: %s\r\n", mkrfc1123(ims));
364 strcat(msg, buf);
090089c4 365 }
62e76326 366
b3b64e58 367 if (max_forwards > -1) {
62e76326 368 snprintf(buf, BUFSIZ, "Max-Forwards: %d\r\n", max_forwards);
369 strcat(msg, buf);
b3b64e58 370 }
62e76326 371
a78886fc 372 if (proxy_user) {
62e76326 373 char *user = proxy_user;
374 char *password = proxy_password;
230c091c 375#if HAVE_GETPASS
62e76326 376
377 if (!password)
378 password = getpass("Proxy password: ");
379
230c091c 380#endif
62e76326 381
382 if (!password) {
383 fprintf(stderr, "ERROR: Proxy password missing\n");
384 exit(1);
385 }
386
387 snprintf(buf, BUFSIZ, "%s:%s", user, password);
a394841f 388 snprintf(buf, BUFSIZ, "Proxy-Authorization: Basic %s\r\n", base64_encode(buf));
62e76326 389 strcat(msg, buf);
a78886fc 390 }
62e76326 391
a78886fc 392 if (www_user) {
62e76326 393 char *user = www_user;
394 char *password = www_password;
230c091c 395#if HAVE_GETPASS
62e76326 396
397 if (!password)
398 password = getpass("WWW password: ");
399
230c091c 400#endif
62e76326 401
402 if (!password) {
403 fprintf(stderr, "ERROR: WWW password missing\n");
404 exit(1);
405 }
406
407 snprintf(buf, BUFSIZ, "%s:%s", user, password);
a394841f 408 snprintf(buf, BUFSIZ, "Authorization: Basic %s\r\n", base64_encode(buf));
62e76326 409 strcat(msg, buf);
a78886fc 410 }
62e76326 411
599eadbe 412 if (keep_alive) {
62e76326 413 if (port != 80)
414 snprintf(buf, BUFSIZ, "Proxy-Connection: keep-alive\r\n");
415 else
416 snprintf(buf, BUFSIZ, "Connection: keep-alive\r\n");
417
418 strcat(msg, buf);
599eadbe 419 }
62e76326 420
63259c34 421 strcat(msg, extra_hdrs);
042461c3 422 snprintf(buf, BUFSIZ, "\r\n");
234967c9 423 strcat(msg, buf);
090089c4 424
63259c34 425 if (opt_verbose)
62e76326 426 fprintf(stderr, "headers: '%s'\n", msg);
63259c34 427
899bab3f 428 if (ping) {
429#if HAVE_SIGACTION
62e76326 430
431 struct sigaction sa, osa;
432
433 if (sigaction(SIGINT, NULL, &osa) == 0 && osa.sa_handler == SIG_DFL) {
434 sa.sa_handler = catchSignal;
435 sa.sa_flags = 0;
436 sigemptyset(&sa.sa_mask);
437 (void) sigaction(SIGINT, &sa, NULL);
438 }
439
899bab3f 440#else
62e76326 441 void (*osig) (int);
442
443 if ((osig = signal(SIGINT, catchSignal)) != SIG_DFL)
444 (void) signal(SIGINT, osig);
445
899bab3f 446#endif
62e76326 447
899bab3f 448 }
62e76326 449
899bab3f 450 loops = ping ? pcount : 1;
62e76326 451
899bab3f 452 for (i = 0; loops == 0 || i < loops; i++) {
62e76326 453 int fsize = 0;
454 /* Connect to the server */
455
456 if ((conn = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
457 perror("client: socket");
458 exit(1);
459 }
460
461 if (localhost && client_comm_bind(conn, localhost) < 0) {
462 perror("client: bind");
463 exit(1);
464 }
465
466 if (client_comm_connect(conn, hostname, port, ping ? &tv1 : NULL) < 0) {
467 if (errno == 0) {
468 fprintf(stderr, "client: ERROR: Cannot connect to %s:%d: Host unknown.\n", hostname, port);
469 } else {
470 char tbuf[BUFSIZ];
471 snprintf(tbuf, BUFSIZ, "client: ERROR: Cannot connect to %s:%d",
472 hostname, port);
473 perror(tbuf);
474 }
475
476 exit(1);
477 }
478
479 /* Send the HTTP request */
62e76326 480 bytesWritten = mywrite(conn, msg, strlen(msg));
481
62e76326 482 if (bytesWritten < 0) {
483 perror("client: ERROR: write");
484 exit(1);
485 } else if ((unsigned) bytesWritten != strlen(msg)) {
486 fprintf(stderr, "client: ERROR: Cannot send request?: %s\n", msg);
487 exit(1);
488 }
489
490 if (put_file) {
491 int x;
492 lseek(put_fd, 0, SEEK_SET);
0ef0f1de 493#ifdef _SQUID_MSWIN_
62e76326 494
495 while ((x = read(put_fd, buf, sizeof(buf))) > 0) {
0ef0f1de 496#else
62e76326 497
498 while ((x = myread(put_fd, buf, sizeof(buf))) > 0) {
0ef0f1de 499#endif
00f768c1 500 x = mywrite(conn, buf, x);
62e76326 501
502 total_bytes += x;
503
504 if (x <= 0)
505 break;
506 }
507
508 if (x != 0)
509 fprintf(stderr, "client: ERROR: Cannot send file.\n");
510 }
511
512 /* Read the data */
54220df8 513
0ef0f1de 514#ifdef _SQUID_MSWIN_
62e76326 515 setmode(1, O_BINARY);
516
00f768c1 517#endif
62e76326 518
519 while ((len = myread(conn, buf, sizeof(buf))) > 0) {
62e76326 520 fsize += len;
521
522 if (to_stdout)
523 fwrite(buf, len, 1, stdout);
524 }
525
0ef0f1de 526#ifdef _SQUID_MSWIN_
62e76326 527 setmode(1, O_TEXT);
528
0ef0f1de 529#endif
62e76326 530
531 (void) close(conn); /* done with socket */
532
533 if (interrupted)
534 break;
535
536 if (ping) {
537
538 struct tm *tmp;
539 time_t t2s;
540 long elapsed_msec;
541
542 (void) Now(&tv2);
543 elapsed_msec = tvSubMsec(tv1, tv2);
544 t2s = tv2.tv_sec;
545 tmp = localtime(&t2s);
546 fprintf(stderr, "%d-%02d-%02d %02d:%02d:%02d [%d]: %ld.%03ld secs, %f KB/s\n",
547 tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
548 tmp->tm_hour, tmp->tm_min, tmp->tm_sec, i + 1,
549 elapsed_msec / 1000, elapsed_msec % 1000,
550 elapsed_msec ? (double) fsize / elapsed_msec : -1.0);
551
552 if (i == 0 || elapsed_msec < ping_min)
553 ping_min = elapsed_msec;
554
555 if (i == 0 || elapsed_msec > ping_max)
556 ping_max = elapsed_msec;
557
558 ping_sum += elapsed_msec;
559
560 /* Delay until next "ping_int" boundary */
561 if ((loops == 0 || i + 1 < loops) && elapsed_msec < ping_int) {
562
563 struct timeval tvs;
564 long msec_left = ping_int - elapsed_msec;
565
566 tvs.tv_sec = msec_left / 1000;
567 tvs.tv_usec = (msec_left % 1000) * 1000;
568 select(0, NULL, NULL, NULL, &tvs);
569 }
570 }
090089c4 571 }
899bab3f 572
573 if (ping && i) {
62e76326 574 ping_mean = ping_sum / i;
575 fprintf(stderr, "%d requests, round-trip (secs) min/avg/max = "
576 "%ld.%03ld/%ld.%03ld/%ld.%03ld\n", i,
577 ping_min / 1000, ping_min % 1000, ping_mean / 1000, ping_mean % 1000,
578 ping_max / 1000, ping_max % 1000);
090089c4 579 }
62e76326 580
090089c4 581 exit(0);
582 /*NOTREACHED */
983061ed 583 return 0;
090089c4 584}
585
2c08acd9 586static int
62e76326 587client_comm_bind(int sock, const char *local_host) {
588
2c08acd9 589 static const struct hostent *hp = NULL;
62e76326 590
2c08acd9 591 static struct sockaddr_in from_addr;
592
593 /* Set up the source socket address from which to send. */
62e76326 594
2c08acd9 595 if (hp == NULL) {
62e76326 596 from_addr.sin_family = AF_INET;
597
598 if ((hp = gethostbyname(local_host)) == 0) {
599 return (-1);
600 }
2c08acd9 601
62e76326 602 xmemcpy(&from_addr.sin_addr, hp->h_addr, hp->h_length);
603 from_addr.sin_port = 0;
2c08acd9 604 }
62e76326 605
2c08acd9 606 return bind(sock, (struct sockaddr *) &from_addr, sizeof(struct sockaddr_in));
607}
608
b8d8561b 609static int
62e76326 610
611client_comm_connect(int sock, const char *dest_host, u_short dest_port, struct timeval *tvp) {
612
899bab3f 613 static const struct hostent *hp = NULL;
62e76326 614
090089c4 615 static struct sockaddr_in to_addr;
616
617 /* Set up the destination socket address for message to send to. */
090089c4 618
62e76326 619 if (hp == NULL)
620 {
621 to_addr.sin_family = AF_INET;
622
623 if ((hp = gethostbyname(dest_host)) == 0) {
624 return (-1);
625 }
626
627 xmemcpy(&to_addr.sin_addr, hp->h_addr, hp->h_length);
628 to_addr.sin_port = htons(dest_port);
090089c4 629 }
62e76326 630
899bab3f 631 if (tvp)
62e76326 632 (void) Now(tvp);
633
090089c4 634 return connect(sock, (struct sockaddr *) &to_addr, sizeof(struct sockaddr_in));
635}
899bab3f 636
637static int
62e76326 638
639Now(struct timeval *tp) {
899bab3f 640#if GETTIMEOFDAY_NO_TZP
641 return gettimeofday(tp);
642#else
62e76326 643
899bab3f 644 return gettimeofday(tp, NULL);
645#endif
646} /* ARGSUSED */
647
648static void
62e76326 649catchSignal(int sig) {
899bab3f 650 interrupted = 1;
651 fprintf(stderr, "Interrupted.\n");
652}
b6c6bcef 653
5446b331 654static void
62e76326 655pipe_handler(int sig) {
b6c6bcef 656 fprintf(stderr, "SIGPIPE received.\n");
54220df8 657}
658
659static void
62e76326 660set_our_signal(void) {
54220df8 661#if HAVE_SIGACTION
62e76326 662
54220df8 663 struct sigaction sa;
664 sa.sa_handler = pipe_handler;
665 sa.sa_flags = SA_RESTART;
666 sigemptyset(&sa.sa_mask);
62e76326 667
54220df8 668 if (sigaction(SIGPIPE, &sa, NULL) < 0) {
62e76326 669 fprintf(stderr, "Cannot set PIPE signal.\n");
670 exit(-1);
54220df8 671 }
62e76326 672
54220df8 673#else
674 signal(SIGPIPE, pipe_handler);
62e76326 675
54220df8 676#endif
677
678}
20cbfe5a 679
680static ssize_t
62e76326 681myread(int fd, void *buf, size_t len) {
00f768c1 682#ifndef _SQUID_MSWIN_
20cbfe5a 683 alarm(io_timeout);
684 return read(fd, buf, len);
00f768c1 685#else
686
687 return recv(fd, buf, len, 0);
688#endif
20cbfe5a 689}
690
691static ssize_t
62e76326 692mywrite(int fd, void *buf, size_t len) {
00f768c1 693#ifndef _SQUID_MSWIN_
20cbfe5a 694 alarm(io_timeout);
695 return write(fd, buf, len);
00f768c1 696#else
62e76326 697
00f768c1 698 return send(fd, buf, len, 0);
0ef0f1de 699#endif
00f768c1 700}