]> git.ipfire.org Git - thirdparty/squid.git/blame - tools/squidclient.cc
Cleanup: base64 coder de-duplication and upgrade
[thirdparty/squid.git] / tools / squidclient.cc
CommitLineData
30a4f2a8 1/*
5ac5029d
AJ
2 * $Id$
3 *
d090e020 4 * DEBUG: section -- WWW Client
30a4f2a8 5 * AUTHOR: Harvest Derived
6 *
2b6662ba 7 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 8 * ----------------------------------------------------------
30a4f2a8 9 *
2b6662ba 10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
30a4f2a8 18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
3afd7aae 23 *
30a4f2a8 24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
3afd7aae 28 *
30a4f2a8 29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
cbdec147 31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 32 *
30a4f2a8 33 */
090089c4 34
94ab55b0 35#include "config.h"
25f98340 36#include "base64.h"
055421ee 37#include "ip/Address.h"
25f98340
AJ
38#include "rfc1123.h"
39#include "SquidTime.h"
94ab55b0 40
15443eec 41#ifdef _SQUID_MSWIN_
63be0a78 42/** \cond AUTODOCS-IGNORE */
15443eec 43using namespace Squid;
63be0a78 44/** \endcond */
15443eec 45#endif
46
be266cb2 47#if _SQUID_WINDOWS_
b55fa77d 48#include <io.h>
49#endif
815f9118 50#if HAVE_STDIO_H
94ab55b0 51#include <stdio.h>
815f9118 52#endif
815f9118 53#if HAVE_SYS_SOCKET_H
94ab55b0 54#include <sys/socket.h>
815f9118 55#endif
56#if HAVE_STRING_H
94ab55b0 57#include <string.h>
815f9118 58#endif
59#if HAVE_UNISTD_H
94ab55b0 60#include <unistd.h>
815f9118 61#endif
489520a9 62#if HAVE_NETDB_H
94ab55b0 63#include <netdb.h>
815f9118 64#endif
65#if HAVE_SIGNAL_H
94ab55b0 66#include <signal.h>
815f9118 67#endif
68#if HAVE_ERRNO_H
94ab55b0 69#include <errno.h>
815f9118 70#endif
71#if HAVE_SYS_STAT_H
94ab55b0 72#include <sys/stat.h>
815f9118 73#endif
74#if HAVE_FCNTL_H
94ab55b0 75#include <fcntl.h>
815f9118 76#endif
77#if HAVE_NETINET_IN_H
c7f83c7a 78#include <netinet/in.h>
815f9118 79#endif
d3e3ff4f 80#if HAVE_GETOPT_H
81#include <getopt.h>
82#endif
94ab55b0 83
823d23e4 84#if HAVE_GSSAPI
c904ede7 85#if HAVE_GSSAPI_GSSAPI_H
823d23e4 86#include <gssapi/gssapi.h>
c904ede7 87#elif HAVE_GSSAPI_H
823d23e4 88#include <gssapi.h>
75a8c92e
AJ
89#endif /* HAVE_GSSAPI_GSSAPI_H/HAVE_GSSAPI_H */
90#if !HAVE_HEIMDAL_KERBEROS
c904ede7 91#if HAVE_GSSAPI_GSSAPI_KRB5_H
823d23e4 92#include <gssapi/gssapi_krb5.h>
75a8c92e 93#endif
c904ede7 94#if HAVE_GSSAPI_GSSAPI_GENERIC_H
823d23e4 95#include <gssapi/gssapi_generic.h>
75a8c92e
AJ
96#endif
97#if HAVE_GSSAPI_GSSAPI_EXT_H
98#include <gssapi/gssapi_ext.h>
99#endif
100#endif
c904ede7
AJ
101
102#ifndef gss_nt_service_name
103#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
823d23e4
AJ
104#endif
105
106#ifndef gss_mech_spnego
107static gss_OID_desc _gss_mech_spnego = {6, (void *) "\x2b\x06\x01\x05\x05\x02"};
108gss_OID gss_mech_spnego = &_gss_mech_spnego;
109#endif
c904ede7 110#endif /* HAVE_GSSAPI */
090089c4 111
112#ifndef BUFSIZ
7ed62376
AJ
113#define BUFSIZ 8192
114#endif
115#ifndef MESSAGELEN
116#define MESSAGELEN 65536
117#endif
118#ifndef HEADERLEN
119#define HEADERLEN 65536
090089c4 120#endif
121
94ab55b0 122typedef void SIGHDLR(int sig);
123
090089c4 124/* Local functions */
b7ac5457 125static int client_comm_bind(int, const Ip::Address &);
62e76326 126
b7ac5457 127static int client_comm_connect(int, const Ip::Address &, struct timeval *);
f5b8bbc4 128static void usage(const char *progname);
62e76326 129
899bab3f 130static int Now(struct timeval *);
daacd51f
AJ
131SIGHDLR catchSignal;
132SIGHDLR pipe_handler;
d6d09e02 133static void set_our_signal(void);
20cbfe5a 134static ssize_t myread(int fd, void *buf, size_t len);
135static ssize_t mywrite(int fd, void *buf, size_t len);
823d23e4
AJ
136
137
138#if HAVE_GSSAPI
139static int check_gss_err(OM_uint32 major_status, OM_uint32 minor_status, const char *function);
140static char *GSSAPI_token(const char *server);
141#endif
142
cca89eeb 143static int put_fd;
144static char *put_file = NULL;
62e76326 145
b6c6bcef 146static struct stat sb;
147int total_bytes = 0;
20cbfe5a 148int io_timeout = 120;
090089c4 149
ec556193
GS
150#ifdef _SQUID_MSWIN_
151void
152Win32SockCleanup(void)
153{
154 WSACleanup();
155 return;
156}
157#endif /* ifdef _SQUID_MSWIN_ */
158
b8d8561b 159static void
0ee4272b 160usage(const char *progname)
090089c4 161{
0ee4272b 162 fprintf(stderr,
3afd7aae 163 "Version: %s\n"
ba7ce724 164 "Usage: %s [-arsv] [-A 'string'] [-g count] [-h remote host] [-H 'string'] [-i IMS] [-I ping-interval] [-j 'Host-header']"
823d23e4
AJ
165 "[-k] [-l local-host] [-m method] "
166#if HAVE_GSSAPI
167 "[-n] [-N] "
168#endif
169 "[-p port] [-P file] [-t count] [-T timeout] [-u proxy-user] [-U www-user] "
4f16e7af
AJ
170 "[-V version] [-w proxy-password] [-W www-password] url\n"
171 "\n"
3afd7aae 172 "Options:\n"
3afd7aae 173 " -a Do NOT include Accept: header.\n"
ba7ce724 174 " -A User-Agent: header. Use \"\" to omit.\n"
4f16e7af 175 " -g count Ping mode, perform \"count\" iterations (0 to loop until interrupted).\n"
3afd7aae 176 " -h host Retrieve URL from cache on hostname. Default is localhost.\n"
4f16e7af
AJ
177 " -H 'string' Extra headers to send. Use '\\n' for new lines.\n"
178 " -i IMS If-Modified-Since time (in Epoch seconds).\n"
179 " -I interval Ping interval in seconds (default 1 second).\n"
5ac5029d 180 " -j hosthdr Host header content\n"
51d94d10 181 " -k Keep the connection active. Default is to do only one request then close.\n"
4f16e7af 182 " -l host Specify a local IP address to bind to. Default is none.\n"
3afd7aae 183 " -m method Request method, default is GET.\n"
823d23e4
AJ
184#if HAVE_GSSAPI
185 " -n Proxy Negotiate(Kerberos) authentication\n"
186 " -N WWW Negotiate(Kerberos) authentication\n"
187#endif
4f16e7af
AJ
188 " -p port Port number of cache. Default is %d.\n"
189 " -P file PUT request. Using the named file\n"
190 " -r Force cache to reload URL.\n"
191 " -s Silent. Do not print data to stdout.\n"
3afd7aae 192 " -t count Trace count cache-hops\n"
3afd7aae
AJ
193 " -T timeout Timeout value (seconds) for read/write operations.\n"
194 " -u user Proxy authentication username\n"
3afd7aae 195 " -U user WWW authentication username\n"
4f16e7af 196 " -v Verbose. Print outgoing message to stderr.\n"
823d23e4 197 " -V version HTTP Version. Use '-' for HTTP/0.9 omitted case\n"
4f16e7af 198 " -w password Proxy authentication password\n"
823d23e4 199 " -W password WWW authentication password\n",
3afd7aae 200 VERSION, progname, CACHE_HTTP_PORT);
090089c4 201 exit(1);
202}
203
899bab3f 204static int interrupted = 0;
b8d8561b 205int
206main(int argc, char *argv[])
090089c4 207{
208 int conn, c, len, bytesWritten;
209 int port, to_stdout, reload;
899bab3f 210 int ping, pcount;
599eadbe 211 int keep_alive = 0;
88738790 212 int opt_noaccept = 0;
63259c34 213 int opt_verbose = 0;
823d23e4 214 int www_neg, proxy_neg;
a2c963ae 215 const char *hostname, *localhost;
b7ac5457 216 Ip::Address iaddr;
7ed62376
AJ
217 char url[BUFSIZ], msg[MESSAGELEN], buf[BUFSIZ];
218 char extra_hdrs[HEADERLEN];
0ee4272b 219 const char *method = "GET";
090089c4 220 extern char *optarg;
234967c9 221 time_t ims = 0;
b3b64e58 222 int max_forwards = -1;
62e76326 223
899bab3f 224 struct timeval tv1, tv2;
225 int i = 0, loops;
226 long ping_int;
227 long ping_min = 0, ping_max = 0, ping_sum = 0, ping_mean = 0;
5ac5029d
AJ
228 const char *proxy_user = NULL;
229 const char *proxy_password = NULL;
230 const char *www_user = NULL;
231 const char *www_password = NULL;
232 const char *host = NULL;
233 const char *version = "1.0";
ba7ce724 234 const char *useragent = NULL;
090089c4 235
236 /* set the defaults */
2c08acd9 237 hostname = "localhost";
238 localhost = NULL;
63259c34 239 extra_hdrs[0] = '\0';
090089c4 240 port = CACHE_HTTP_PORT;
241 to_stdout = 1;
242 reload = 0;
899bab3f 243 ping = 0;
244 pcount = 0;
245 ping_int = 1 * 1000;
823d23e4
AJ
246 www_neg = 0;
247 proxy_neg = 0;
090089c4 248
249 if (argc < 2) {
3afd7aae 250 usage(argv[0]); /* need URL */
090089c4 251 } else if (argc >= 2) {
3afd7aae
AJ
252 strncpy(url, argv[argc - 1], BUFSIZ);
253 url[BUFSIZ - 1] = '\0';
62e76326 254
3afd7aae
AJ
255 if (url[0] == '-')
256 usage(argv[0]);
823d23e4 257#if HAVE_GSSAPI
ba7ce724 258 while ((c = getopt(argc, argv, "aA:h:j:V:l:P:i:km:p:rsvt:g:p:I:H:T:u:U:w:W:nN?")) != -1)
823d23e4 259#else
ba7ce724 260 while ((c = getopt(argc, argv, "aA:h:j:V:l:P:i:km:p:rsvt:g:p:I:H:T:u:U:w:W:?")) != -1)
823d23e4 261#endif
3afd7aae 262 switch (c) {
62e76326 263
3afd7aae
AJ
264 case 'a':
265 opt_noaccept = 1;
266 break;
62e76326 267
ba7ce724
AJ
268 case 'A':
269 if (optarg != NULL)
270 useragent = optarg;
271 break;
272
3afd7aae 273 case 'h': /* remote host */
3afd7aae
AJ
274 if (optarg != NULL)
275 hostname = optarg;
5ac5029d 276 break;
62e76326 277
5ac5029d 278 case 'j':
af6a12ee
AJ
279 host = optarg;
280 break;
5ac5029d
AJ
281
282 case 'V':
283 if (optarg != NULL)
284 version = optarg;
3afd7aae 285 break;
62e76326 286
3afd7aae
AJ
287 case 'l': /* local host */
288 if (optarg != NULL)
289 localhost = optarg;
3afd7aae 290 break;
62e76326 291
3afd7aae
AJ
292 case 's': /* silent */
293 to_stdout = 0;
3afd7aae 294 break;
62e76326 295
3afd7aae
AJ
296 case 'k': /* backward compat */
297 keep_alive = 1;
3afd7aae 298 break;
62e76326 299
3afd7aae
AJ
300 case 'r': /* reload */
301 reload = 1;
3afd7aae 302 break;
62e76326 303
3afd7aae
AJ
304 case 'p': /* port number */
305 sscanf(optarg, "%d", &port);
3afd7aae
AJ
306 if (port < 1)
307 port = CACHE_HTTP_PORT; /* default */
3afd7aae 308 break;
62e76326 309
3afd7aae
AJ
310 case 'P':
311 put_file = xstrdup(optarg);
3afd7aae 312 break;
62e76326 313
3afd7aae
AJ
314 case 'i': /* IMS */
315 ims = (time_t) atoi(optarg);
3afd7aae 316 break;
62e76326 317
3afd7aae
AJ
318 case 'm':
319 method = xstrdup(optarg);
3afd7aae 320 break;
62e76326 321
3afd7aae
AJ
322 case 't':
323 method = xstrdup("TRACE");
3afd7aae 324 max_forwards = atoi(optarg);
3afd7aae 325 break;
62e76326 326
3afd7aae
AJ
327 case 'g':
328 ping = 1;
3afd7aae 329 pcount = atoi(optarg);
3afd7aae 330 to_stdout = 0;
3afd7aae 331 break;
62e76326 332
3afd7aae
AJ
333 case 'I':
334 if ((ping_int = atoi(optarg) * 1000) <= 0)
335 usage(argv[0]);
3afd7aae 336 break;
62e76326 337
3afd7aae
AJ
338 case 'H':
339 if (strlen(optarg)) {
340 char *t;
341 strncpy(extra_hdrs, optarg, sizeof(extra_hdrs));
3afd7aae
AJ
342 while ((t = strstr(extra_hdrs, "\\n")))
343 *t = '\r', *(t + 1) = '\n';
344 }
345 break;
62e76326 346
3afd7aae
AJ
347 case 'T':
348 io_timeout = atoi(optarg);
349 break;
62e76326 350
3afd7aae
AJ
351 case 'u':
352 proxy_user = optarg;
353 break;
62e76326 354
3afd7aae
AJ
355 case 'w':
356 proxy_password = optarg;
357 break;
62e76326 358
3afd7aae
AJ
359 case 'U':
360 www_user = optarg;
361 break;
62e76326 362
3afd7aae
AJ
363 case 'W':
364 www_password = optarg;
365 break;
62e76326 366
823d23e4
AJ
367#if HAVE_GSSAPI
368 case 'n':
369 proxy_neg = 1;
370 break;
371
372 case 'N':
373 www_neg = 1;
374 break;
375#endif
3afd7aae
AJ
376 case 'v':
377 /* undocumented: may increase verb-level by giving more -v's */
378 opt_verbose++;
379 break;
62e76326 380
3afd7aae 381 case '?': /* usage */
62e76326 382
3afd7aae
AJ
383 default:
384 usage(argv[0]);
385 break;
386 }
090089c4 387 }
0ef0f1de 388#ifdef _SQUID_MSWIN_
389 {
3afd7aae
AJ
390 WSADATA wsaData;
391 WSAStartup(2, &wsaData);
392 atexit(Win32SockCleanup);
0ef0f1de 393 }
394#endif
090089c4 395 /* Build the HTTP request */
8a9b6b94 396 if (strncmp(url, "mgr:", 4) == 0) {
3afd7aae 397 char *t = xstrdup(url + 4);
3f3e5473
AJ
398 const char *at = NULL;
399 if (!strrchr(t, '@')) { // ignore any -w password if @ is explicit already.
400 at = proxy_password;
401 }
402 // embed the -w proxy password into old-style cachemgr URLs
403 if (at)
404 snprintf(url, BUFSIZ, "cache_object://%s/%s@%s", hostname, t, at);
405 else
406 snprintf(url, BUFSIZ, "cache_object://%s/%s", hostname, t);
3afd7aae 407 xfree(t);
8a9b6b94 408 }
cca89eeb 409 if (put_file) {
3afd7aae
AJ
410 put_fd = open(put_file, O_RDONLY);
411 set_our_signal();
412
413 if (put_fd < 0) {
414 fprintf(stderr, "%s: can't open file (%s)\n", argv[0],
415 xstrerror());
416 exit(-1);
417 }
be266cb2 418#if _SQUID_WINDOWS_
3afd7aae 419 setmode(put_fd, O_BINARY);
c4aefe96 420#endif
62e76326 421
3afd7aae 422 fstat(put_fd, &sb);
cca89eeb 423 }
5ac5029d
AJ
424
425 if (!host) {
af6a12ee
AJ
426 char *newhost = strstr(url, "://");
427 if (newhost) {
428 char *t;
429 newhost += 3;
dc47f531 430 newhost = xstrdup(newhost);
af6a12ee
AJ
431 t = newhost + strcspn(newhost, "@/?");
432 if (*t == '@') {
433 newhost = t + 1;
434 t = newhost + strcspn(newhost, "@/?");
435 }
436 *t = '\0';
437 host = newhost;
438 }
5ac5029d
AJ
439 }
440
af6a12ee
AJ
441 if (version[0] == '-' || !version[0] || version[0] == '0') {
442 /* HTTP/0.9, no headers, no version */
5ac5029d
AJ
443 snprintf(msg, BUFSIZ, "%s %s\r\n", method, url);
444 } else {
445 snprintf(msg, BUFSIZ, "%s %s HTTP/%s\r\n", method, url, version);
62e76326 446
af6a12ee
AJ
447 if (host) {
448 snprintf(buf, BUFSIZ, "Host: %s\r\n", host);
449 strcat(msg,buf);
450 }
451
ba7ce724
AJ
452 if (useragent == NULL) {
453 snprintf(buf, BUFSIZ, "User-Agent: squidclient/%s\r\n", VERSION);
454 strcat(msg,buf);
455 } else if (useragent[0] != '\0') {
456 snprintf(buf, BUFSIZ, "User-Agent: %s\r\n", useragent);
457 strcat(msg,buf);
458 }
459
af6a12ee 460 if (reload) {
d440ba10 461 snprintf(buf, BUFSIZ, "Cache-Control: no-cache\r\n");
af6a12ee
AJ
462 strcat(msg, buf);
463 }
464 if (put_fd > 0) {
173bc2bf 465 snprintf(buf, BUFSIZ, "Content-length: %" PRId64 "\r\n", (int64_t) sb.st_size);
af6a12ee
AJ
466 strcat(msg, buf);
467 }
468 if (opt_noaccept == 0) {
469 snprintf(buf, BUFSIZ, "Accept: */*\r\n");
470 strcat(msg, buf);
471 }
472 if (ims) {
473 snprintf(buf, BUFSIZ, "If-Modified-Since: %s\r\n", mkrfc1123(ims));
474 strcat(msg, buf);
475 }
476 if (max_forwards > -1) {
477 snprintf(buf, BUFSIZ, "Max-Forwards: %d\r\n", max_forwards);
478 strcat(msg, buf);
479 }
480 if (proxy_user) {
481 const char *user = proxy_user;
482 const char *password = proxy_password;
230c091c 483#if HAVE_GETPASS
af6a12ee
AJ
484 if (!password)
485 password = getpass("Proxy password: ");
230c091c 486#endif
af6a12ee
AJ
487 if (!password) {
488 fprintf(stderr, "ERROR: Proxy password missing\n");
489 exit(1);
490 }
491 snprintf(buf, BUFSIZ, "%s:%s", user, password);
8bdd0cec 492 snprintf(buf, BUFSIZ, "Proxy-Authorization: Basic %s\r\n", old_base64_encode(buf));
af6a12ee
AJ
493 strcat(msg, buf);
494 }
495 if (www_user) {
496 const char *user = www_user;
497 const char *password = www_password;
230c091c 498#if HAVE_GETPASS
af6a12ee
AJ
499 if (!password)
500 password = getpass("WWW password: ");
230c091c 501#endif
af6a12ee
AJ
502 if (!password) {
503 fprintf(stderr, "ERROR: WWW password missing\n");
504 exit(1);
505 }
506 snprintf(buf, BUFSIZ, "%s:%s", user, password);
8bdd0cec 507 snprintf(buf, BUFSIZ, "Authorization: Basic %s\r\n", old_base64_encode(buf));
af6a12ee
AJ
508 strcat(msg, buf);
509 }
823d23e4
AJ
510#if HAVE_GSSAPI
511 if (www_neg) {
512 if (host) {
513 snprintf(buf, BUFSIZ, "Authorization: Negotiate %s\r\n", GSSAPI_token(host));
514 strcat(msg, buf);
515 } else
516 fprintf(stderr, "ERROR: server host missing\n");
517 }
518 if (proxy_neg) {
519 if (hostname) {
520 snprintf(buf, BUFSIZ, "Proxy-Authorization: Negotiate %s\r\n", GSSAPI_token(hostname));
521 strcat(msg, buf);
522 } else
523 fprintf(stderr, "ERROR: proxy server host missing\n");
524 }
525#endif
af6a12ee 526
95e78500
AJ
527 /* HTTP/1.0 may need keep-alive explicitly */
528 if (strcmp(version, "1.0") == 0 && keep_alive)
529 strcat(msg, "Connection: keep-alive\r\n");
530
531 /* HTTP/1.1 may need close explicitly */
51d94d10
AJ
532 if (!keep_alive)
533 strcat(msg, "Connection: close\r\n");
af6a12ee
AJ
534
535 strcat(msg, extra_hdrs);
536 strcat(msg, "\r\n");
a78886fc 537 }
5ac5029d 538
63259c34 539 if (opt_verbose)
5d9acded 540 fprintf(stderr, "Request:'%s'\n", msg);
63259c34 541
899bab3f 542 if (ping) {
543#if HAVE_SIGACTION
62e76326 544
3afd7aae 545 struct sigaction sa, osa;
62e76326 546
3afd7aae
AJ
547 if (sigaction(SIGINT, NULL, &osa) == 0 && osa.sa_handler == SIG_DFL) {
548 sa.sa_handler = catchSignal;
549 sa.sa_flags = 0;
550 sigemptyset(&sa.sa_mask);
551 (void) sigaction(SIGINT, &sa, NULL);
552 }
899bab3f 553#else
3afd7aae 554 void (*osig) (int);
62e76326 555
3afd7aae
AJ
556 if ((osig = signal(SIGINT, catchSignal)) != SIG_DFL)
557 (void) signal(SIGINT, osig);
62e76326 558
899bab3f 559#endif
62e76326 560
899bab3f 561 }
562 loops = ping ? pcount : 1;
62e76326 563
899bab3f 564 for (i = 0; loops == 0 || i < loops; i++) {
3afd7aae 565 int fsize = 0;
cc192b50 566 struct addrinfo *AI = NULL;
567
5d9acded
AJ
568 if (opt_verbose)
569 fprintf(stderr, "Resolving... %s\n", hostname);
570
3afd7aae 571 /* Connect to the server */
988e90e1 572
3afd7aae
AJ
573 if (localhost) {
574 if ( !iaddr.GetHostByName(localhost) ) {
cc192b50 575 fprintf(stderr, "client: ERROR: Cannot resolve %s: Host unknown.\n", localhost);
576 exit(1);
577 }
3afd7aae 578 } else {
cc192b50 579 /* Process the remote host name to locate the Protocol required
580 in case we are being asked to link to another version of squid */
3afd7aae 581 if ( !iaddr.GetHostByName(hostname) ) {
cc192b50 582 fprintf(stderr, "client: ERROR: Cannot resolve %s: Host unknown.\n", hostname);
583 exit(1);
584 }
585 }
586
587 iaddr.GetAddrInfo(AI);
588 if ((conn = socket(AI->ai_family, AI->ai_socktype, 0)) < 0) {
589 perror("client: socket");
590 iaddr.FreeAddrInfo(AI);
591 exit(1);
592 }
593 iaddr.FreeAddrInfo(AI);
594
595 if (localhost && client_comm_bind(conn, iaddr) < 0) {
596 perror("client: bind");
597 exit(1);
598 }
599
600 iaddr.SetEmpty();
3afd7aae 601 if ( !iaddr.GetHostByName(hostname) ) {
cc192b50 602 fprintf(stderr, "client: ERROR: Cannot resolve %s: Host unknown.\n", hostname);
603 exit(1);
604 }
605
606 iaddr.SetPort(port);
607
5d9acded
AJ
608 if (opt_verbose) {
609 char ipbuf[MAX_IPSTRLEN];
610 fprintf(stderr, "Connecting... %s(%s)\n", hostname, iaddr.NtoA(ipbuf, MAX_IPSTRLEN));
611 }
612
cc192b50 613 if (client_comm_connect(conn, iaddr, ping ? &tv1 : NULL) < 0) {
e053c141
FC
614 char hostnameBuf[MAX_IPSTRLEN];
615 iaddr.ToURL(hostnameBuf, MAX_IPSTRLEN);
cc192b50 616 if (errno == 0) {
e053c141 617 fprintf(stderr, "client: ERROR: Cannot connect to %s: Host unknown.\n", hostnameBuf);
cc192b50 618 } else {
619 char tbuf[BUFSIZ];
e053c141 620 snprintf(tbuf, BUFSIZ, "client: ERROR: Cannot connect to %s", hostnameBuf);
cc192b50 621 perror(tbuf);
622 }
623 exit(1);
624 }
5d9acded
AJ
625 if (opt_verbose) {
626 char ipbuf[MAX_IPSTRLEN];
627 fprintf(stderr, "Connected to: %s (%s)\n", hostname, iaddr.NtoA(ipbuf, MAX_IPSTRLEN));
628 }
988e90e1 629
3afd7aae
AJ
630 /* Send the HTTP request */
631 bytesWritten = mywrite(conn, msg, strlen(msg));
988e90e1 632
3afd7aae
AJ
633 if (bytesWritten < 0) {
634 perror("client: ERROR: write");
635 exit(1);
636 } else if ((unsigned) bytesWritten != strlen(msg)) {
637 fprintf(stderr, "client: ERROR: Cannot send request?: %s\n", msg);
638 exit(1);
639 }
cc192b50 640
3afd7aae
AJ
641 if (put_file) {
642 int x;
643 lseek(put_fd, 0, SEEK_SET);
0ef0f1de 644#ifdef _SQUID_MSWIN_
62e76326 645
3afd7aae 646 while ((x = read(put_fd, buf, sizeof(buf))) > 0) {
0ef0f1de 647#else
62e76326 648
3afd7aae 649 while ((x = myread(put_fd, buf, sizeof(buf))) > 0) {
0ef0f1de 650#endif
3afd7aae 651 x = mywrite(conn, buf, x);
62e76326 652
3afd7aae 653 total_bytes += x;
62e76326 654
3afd7aae
AJ
655 if (x <= 0)
656 break;
657 }
62e76326 658
3afd7aae
AJ
659 if (x != 0)
660 fprintf(stderr, "client: ERROR: Cannot send file.\n");
661 }
662 /* Read the data */
54220df8 663
0ef0f1de 664#ifdef _SQUID_MSWIN_
3afd7aae 665 setmode(1, O_BINARY);
62e76326 666
00f768c1 667#endif
62e76326 668
3afd7aae
AJ
669 while ((len = myread(conn, buf, sizeof(buf))) > 0) {
670 fsize += len;
62e76326 671
8fee788b
AR
672 if (to_stdout && fwrite(buf, len, 1, stdout) != 1)
673 perror("client: ERROR writing to stdout");
3afd7aae 674 }
62e76326 675
0ef0f1de 676#ifdef _SQUID_MSWIN_
3afd7aae 677 setmode(1, O_TEXT);
62e76326 678
0ef0f1de 679#endif
62e76326 680
3afd7aae 681 (void) close(conn); /* done with socket */
62e76326 682
3afd7aae
AJ
683 if (interrupted)
684 break;
62e76326 685
3afd7aae 686 if (ping) {
62e76326 687
3afd7aae
AJ
688 struct tm *tmp;
689 time_t t2s;
690 long elapsed_msec;
62e76326 691
3afd7aae
AJ
692 (void) Now(&tv2);
693 elapsed_msec = tvSubMsec(tv1, tv2);
694 t2s = tv2.tv_sec;
695 tmp = localtime(&t2s);
696 fprintf(stderr, "%d-%02d-%02d %02d:%02d:%02d [%d]: %ld.%03ld secs, %f KB/s\n",
697 tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
698 tmp->tm_hour, tmp->tm_min, tmp->tm_sec, i + 1,
699 elapsed_msec / 1000, elapsed_msec % 1000,
700 elapsed_msec ? (double) fsize / elapsed_msec : -1.0);
62e76326 701
3afd7aae
AJ
702 if (i == 0 || elapsed_msec < ping_min)
703 ping_min = elapsed_msec;
62e76326 704
3afd7aae
AJ
705 if (i == 0 || elapsed_msec > ping_max)
706 ping_max = elapsed_msec;
62e76326 707
3afd7aae 708 ping_sum += elapsed_msec;
62e76326 709
3afd7aae
AJ
710 /* Delay until next "ping_int" boundary */
711 if ((loops == 0 || i + 1 < loops) && elapsed_msec < ping_int) {
62e76326 712
3afd7aae
AJ
713 struct timeval tvs;
714 long msec_left = ping_int - elapsed_msec;
62e76326 715
3afd7aae
AJ
716 tvs.tv_sec = msec_left / 1000;
717 tvs.tv_usec = (msec_left % 1000) * 1000;
718 select(0, NULL, NULL, NULL, &tvs);
719 }
720 }
090089c4 721 }
899bab3f 722
723 if (ping && i) {
3afd7aae
AJ
724 ping_mean = ping_sum / i;
725 fprintf(stderr, "%d requests, round-trip (secs) min/avg/max = "
726 "%ld.%03ld/%ld.%03ld/%ld.%03ld\n", i,
727 ping_min / 1000, ping_min % 1000, ping_mean / 1000, ping_mean % 1000,
728 ping_max / 1000, ping_max % 1000);
090089c4 729 }
090089c4 730 exit(0);
731 /*NOTREACHED */
983061ed 732 return 0;
090089c4 733}
734
2c08acd9 735static int
b7ac5457 736client_comm_bind(int sock, const Ip::Address &addr)
e1381638 737{
62e76326 738
cc192b50 739 int res;
62e76326 740
cc192b50 741 static struct addrinfo *AI = NULL;
2c08acd9 742
743 /* Set up the source socket address from which to send. */
62e76326 744
cc192b50 745 addr.GetAddrInfo(AI);
2c08acd9 746
cc192b50 747 res = bind(sock, AI->ai_addr, AI->ai_addrlen);
748
749 addr.FreeAddrInfo(AI);
750
751 return res;
2c08acd9 752}
753
b8d8561b 754static int
b7ac5457 755client_comm_connect(int sock, const Ip::Address &addr, struct timeval *tvp)
e1381638 756{
cc192b50 757 int res;
758 static struct addrinfo *AI = NULL;
62e76326 759
cc192b50 760 /* Set up the destination socket address for message to send to. */
62e76326 761
cc192b50 762 addr.GetAddrInfo(AI);
090089c4 763
cc192b50 764 res = connect(sock, AI->ai_addr, AI->ai_addrlen);
090089c4 765
cc192b50 766 addr.FreeAddrInfo(AI);
62e76326 767
899bab3f 768 if (tvp)
cc192b50 769 (void) Now(tvp);
62e76326 770
cc192b50 771 return res;
090089c4 772}
899bab3f 773
774static int
e1381638
AJ
775Now(struct timeval *tp)
776{
899bab3f 777#if GETTIMEOFDAY_NO_TZP
778 return gettimeofday(tp);
779#else
62e76326 780
899bab3f 781 return gettimeofday(tp, NULL);
782#endif
783} /* ARGSUSED */
784
daacd51f 785void
e1381638
AJ
786catchSignal(int sig)
787{
899bab3f 788 interrupted = 1;
789 fprintf(stderr, "Interrupted.\n");
790}
b6c6bcef 791
daacd51f 792void
e1381638
AJ
793pipe_handler(int sig)
794{
b6c6bcef 795 fprintf(stderr, "SIGPIPE received.\n");
54220df8 796}
797
798static void
e1381638
AJ
799set_our_signal(void)
800{
54220df8 801#if HAVE_SIGACTION
62e76326 802
54220df8 803 struct sigaction sa;
804 sa.sa_handler = pipe_handler;
805 sa.sa_flags = SA_RESTART;
806 sigemptyset(&sa.sa_mask);
62e76326 807
54220df8 808 if (sigaction(SIGPIPE, &sa, NULL) < 0) {
3afd7aae
AJ
809 fprintf(stderr, "Cannot set PIPE signal.\n");
810 exit(-1);
54220df8 811 }
812#else
813 signal(SIGPIPE, pipe_handler);
62e76326 814
54220df8 815#endif
816
817}
20cbfe5a 818
819static ssize_t
e1381638
AJ
820myread(int fd, void *buf, size_t len)
821{
00f768c1 822#ifndef _SQUID_MSWIN_
20cbfe5a 823 alarm(io_timeout);
824 return read(fd, buf, len);
00f768c1 825#else
826
827 return recv(fd, buf, len, 0);
828#endif
20cbfe5a 829}
830
831static ssize_t
e1381638
AJ
832mywrite(int fd, void *buf, size_t len)
833{
00f768c1 834#ifndef _SQUID_MSWIN_
20cbfe5a 835 alarm(io_timeout);
836 return write(fd, buf, len);
00f768c1 837#else
62e76326 838
00f768c1 839 return send(fd, buf, len, 0);
0ef0f1de 840#endif
00f768c1 841}
823d23e4
AJ
842
843#if HAVE_GSSAPI
844/*
845 * Check return valuse major_status, minor_status for error and print error description
846 * in case of an error.
847 * Returns 1 in case of gssapi error
848 * 0 in case of no gssapi error
849 */
850#define BUFFER_SIZE 8192
851static int
852check_gss_err(OM_uint32 major_status, OM_uint32 minor_status, const char *function)
853{
854 if (GSS_ERROR(major_status)) {
855 OM_uint32 maj_stat, min_stat;
856 OM_uint32 msg_ctx = 0;
857 gss_buffer_desc status_string;
858 char buf[BUFFER_SIZE];
859 size_t len;
860
861 len = 0;
862 msg_ctx = 0;
863 while (!msg_ctx) {
864 /* convert major status code (GSS-API error) to text */
865 maj_stat = gss_display_status(&min_stat, major_status,
866 GSS_C_GSS_CODE,
867 GSS_C_NULL_OID,
868 &msg_ctx, &status_string);
869 if (maj_stat == GSS_S_COMPLETE) {
870 snprintf(buf + len, BUFFER_SIZE-len, "%s", (char *) status_string.value);
871 len += status_string.length;
872 gss_release_buffer(&min_stat, &status_string);
873 break;
874 }
875 gss_release_buffer(&min_stat, &status_string);
876 }
877 snprintf(buf + len, BUFFER_SIZE-len, "%s", ". ");
878 len += 2;
879 msg_ctx = 0;
880 while (!msg_ctx) {
881 /* convert minor status code (underlying routine error) to text */
882 maj_stat = gss_display_status(&min_stat, minor_status,
883 GSS_C_MECH_CODE,
884 GSS_C_NULL_OID,
885 &msg_ctx, &status_string);
886 if (maj_stat == GSS_S_COMPLETE) {
887 snprintf(buf + len, BUFFER_SIZE-len,"%s", (char *) status_string.value);
888 len += status_string.length;
889 gss_release_buffer(&min_stat, &status_string);
890 break;
891 }
892 gss_release_buffer(&min_stat, &status_string);
893 }
894 fprintf(stderr, "%s failed: %s\n", function, buf);
895 return (1);
896 }
897 return (0);
898}
899
900/*
901 * Get gssapi token for service HTTP/<server>
902 * User has to initiate a kinit user@DOMAIN on commandline first for the
903 * function to be successful
904 * Returns base64 encoded token if successful
905 * string "ERROR" if unsuccessful
906 */
907static char *
908GSSAPI_token(const char *server)
909{
910 OM_uint32 major_status, minor_status;
911 gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
912 gss_name_t server_name = GSS_C_NO_NAME;
913 gss_buffer_desc service = GSS_C_EMPTY_BUFFER;
914 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
915 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
916 char *token = NULL;
917
918 setbuf(stdout, NULL);
919 setbuf(stdin, NULL);
920
921 if (!server) {
922 fprintf(stderr, "Error: No server name\n");
923 return (char *)"ERROR";
924 }
925 service.value = xmalloc(strlen("HTTP") + strlen(server) + 2);
926 snprintf((char *) service.value, strlen("HTTP") + strlen(server) + 2, "%s@%s", "HTTP", server);
927 service.length = strlen((char *) service.value);
928
929 major_status = gss_import_name(&minor_status, &service,
930 gss_nt_service_name, &server_name);
931
932 if (!check_gss_err(major_status, minor_status, "gss_import_name()")) {
933
934 major_status = gss_init_sec_context(&minor_status,
935 GSS_C_NO_CREDENTIAL,
936 &gss_context,
937 server_name,
938 gss_mech_spnego,
939 0,
940 0,
941 GSS_C_NO_CHANNEL_BINDINGS,
942 &input_token,
943 NULL,
944 &output_token,
945 NULL,
946 NULL);
947
948 if (!check_gss_err(major_status, minor_status, "gss_init_sec_context()")) {
949
950 if (output_token.length)
951 token = (char *) base64_encode_bin((const char *) output_token.value, output_token.length);
952 }
953 }
954
955 if (!output_token.length)
956 token = (char *) "ERROR";
957 gss_delete_sec_context(&minor_status, &gss_context, NULL);
958 gss_release_buffer(&minor_status, &service);
959 gss_release_buffer(&minor_status, &input_token);
960 gss_release_buffer(&minor_status, &output_token);
961 gss_release_name(&minor_status, &server_name);
962
963 return token;
964}
965#endif