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