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