]>
Commit | Line | Data |
---|---|---|
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 | 43 | using 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 | |
107 | static gss_OID_desc _gss_mech_spnego = {6, (void *) "\x2b\x06\x01\x05\x05\x02"}; | |
108 | gss_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 | 122 | typedef void SIGHDLR(int sig); |
123 | ||
090089c4 | 124 | /* Local functions */ |
b7ac5457 | 125 | static int client_comm_bind(int, const Ip::Address &); |
62e76326 | 126 | |
b7ac5457 | 127 | static int client_comm_connect(int, const Ip::Address &, struct timeval *); |
f5b8bbc4 | 128 | static void usage(const char *progname); |
62e76326 | 129 | |
899bab3f | 130 | static int Now(struct timeval *); |
daacd51f AJ |
131 | SIGHDLR catchSignal; |
132 | SIGHDLR pipe_handler; | |
d6d09e02 | 133 | static void set_our_signal(void); |
20cbfe5a | 134 | static ssize_t myread(int fd, void *buf, size_t len); |
135 | static ssize_t mywrite(int fd, void *buf, size_t len); | |
823d23e4 AJ |
136 | |
137 | ||
138 | #if HAVE_GSSAPI | |
139 | static int check_gss_err(OM_uint32 major_status, OM_uint32 minor_status, const char *function); | |
140 | static char *GSSAPI_token(const char *server); | |
141 | #endif | |
142 | ||
cca89eeb | 143 | static int put_fd; |
144 | static char *put_file = NULL; | |
62e76326 | 145 | |
b6c6bcef | 146 | static struct stat sb; |
147 | int total_bytes = 0; | |
20cbfe5a | 148 | int io_timeout = 120; |
090089c4 | 149 | |
ec556193 GS |
150 | #ifdef _SQUID_MSWIN_ |
151 | void | |
152 | Win32SockCleanup(void) | |
153 | { | |
154 | WSACleanup(); | |
155 | return; | |
156 | } | |
157 | #endif /* ifdef _SQUID_MSWIN_ */ | |
158 | ||
b8d8561b | 159 | static void |
0ee4272b | 160 | usage(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 | 204 | static int interrupted = 0; |
b8d8561b | 205 | int |
206 | main(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 | 735 | static int |
b7ac5457 | 736 | client_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 | 754 | static int |
b7ac5457 | 755 | client_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 | |
774 | static int | |
e1381638 AJ |
775 | Now(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 | 785 | void |
e1381638 AJ |
786 | catchSignal(int sig) |
787 | { | |
899bab3f | 788 | interrupted = 1; |
789 | fprintf(stderr, "Interrupted.\n"); | |
790 | } | |
b6c6bcef | 791 | |
daacd51f | 792 | void |
e1381638 AJ |
793 | pipe_handler(int sig) |
794 | { | |
b6c6bcef | 795 | fprintf(stderr, "SIGPIPE received.\n"); |
54220df8 | 796 | } |
797 | ||
798 | static void | |
e1381638 AJ |
799 | set_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 | |
819 | static ssize_t | |
e1381638 AJ |
820 | myread(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 | ||
831 | static ssize_t | |
e1381638 AJ |
832 | mywrite(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 | |
851 | static int | |
852 | check_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 | */ | |
907 | static char * | |
908 | GSSAPI_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 |