]>
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 | ||
b55fa77d | 47 | #ifdef _SQUID_WIN32_ |
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" |
4f16e7af | 164 | "Usage: %s [-arsv] [-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" |
4f16e7af | 174 | " -g count Ping mode, perform \"count\" iterations (0 to loop until interrupted).\n" |
3afd7aae | 175 | " -h host Retrieve URL from cache on hostname. Default is localhost.\n" |
4f16e7af AJ |
176 | " -H 'string' Extra headers to send. Use '\\n' for new lines.\n" |
177 | " -i IMS If-Modified-Since time (in Epoch seconds).\n" | |
178 | " -I interval Ping interval in seconds (default 1 second).\n" | |
5ac5029d | 179 | " -j hosthdr Host header content\n" |
51d94d10 | 180 | " -k Keep the connection active. Default is to do only one request then close.\n" |
4f16e7af | 181 | " -l host Specify a local IP address to bind to. Default is none.\n" |
3afd7aae | 182 | " -m method Request method, default is GET.\n" |
823d23e4 AJ |
183 | #if HAVE_GSSAPI |
184 | " -n Proxy Negotiate(Kerberos) authentication\n" | |
185 | " -N WWW Negotiate(Kerberos) authentication\n" | |
186 | #endif | |
4f16e7af AJ |
187 | " -p port Port number of cache. Default is %d.\n" |
188 | " -P file PUT request. Using the named file\n" | |
189 | " -r Force cache to reload URL.\n" | |
190 | " -s Silent. Do not print data to stdout.\n" | |
3afd7aae | 191 | " -t count Trace count cache-hops\n" |
3afd7aae AJ |
192 | " -T timeout Timeout value (seconds) for read/write operations.\n" |
193 | " -u user Proxy authentication username\n" | |
3afd7aae | 194 | " -U user WWW authentication username\n" |
4f16e7af | 195 | " -v Verbose. Print outgoing message to stderr.\n" |
823d23e4 | 196 | " -V version HTTP Version. Use '-' for HTTP/0.9 omitted case\n" |
4f16e7af | 197 | " -w password Proxy authentication password\n" |
823d23e4 | 198 | " -W password WWW authentication password\n", |
3afd7aae | 199 | VERSION, progname, CACHE_HTTP_PORT); |
090089c4 | 200 | exit(1); |
201 | } | |
202 | ||
899bab3f | 203 | static int interrupted = 0; |
b8d8561b | 204 | int |
205 | main(int argc, char *argv[]) | |
090089c4 | 206 | { |
207 | int conn, c, len, bytesWritten; | |
208 | int port, to_stdout, reload; | |
899bab3f | 209 | int ping, pcount; |
599eadbe | 210 | int keep_alive = 0; |
88738790 | 211 | int opt_noaccept = 0; |
63259c34 | 212 | int opt_verbose = 0; |
823d23e4 | 213 | int www_neg, proxy_neg; |
a2c963ae | 214 | const char *hostname, *localhost; |
b7ac5457 | 215 | Ip::Address iaddr; |
7ed62376 AJ |
216 | char url[BUFSIZ], msg[MESSAGELEN], buf[BUFSIZ]; |
217 | char extra_hdrs[HEADERLEN]; | |
0ee4272b | 218 | const char *method = "GET"; |
090089c4 | 219 | extern char *optarg; |
234967c9 | 220 | time_t ims = 0; |
b3b64e58 | 221 | int max_forwards = -1; |
62e76326 | 222 | |
899bab3f | 223 | struct timeval tv1, tv2; |
224 | int i = 0, loops; | |
225 | long ping_int; | |
226 | long ping_min = 0, ping_max = 0, ping_sum = 0, ping_mean = 0; | |
5ac5029d AJ |
227 | const char *proxy_user = NULL; |
228 | const char *proxy_password = NULL; | |
229 | const char *www_user = NULL; | |
230 | const char *www_password = NULL; | |
231 | const char *host = NULL; | |
232 | const char *version = "1.0"; | |
090089c4 | 233 | |
234 | /* set the defaults */ | |
2c08acd9 | 235 | hostname = "localhost"; |
236 | localhost = NULL; | |
63259c34 | 237 | extra_hdrs[0] = '\0'; |
090089c4 | 238 | port = CACHE_HTTP_PORT; |
239 | to_stdout = 1; | |
240 | reload = 0; | |
899bab3f | 241 | ping = 0; |
242 | pcount = 0; | |
243 | ping_int = 1 * 1000; | |
823d23e4 AJ |
244 | www_neg = 0; |
245 | proxy_neg = 0; | |
090089c4 | 246 | |
247 | if (argc < 2) { | |
3afd7aae | 248 | usage(argv[0]); /* need URL */ |
090089c4 | 249 | } else if (argc >= 2) { |
3afd7aae AJ |
250 | strncpy(url, argv[argc - 1], BUFSIZ); |
251 | url[BUFSIZ - 1] = '\0'; | |
62e76326 | 252 | |
3afd7aae AJ |
253 | if (url[0] == '-') |
254 | usage(argv[0]); | |
823d23e4 AJ |
255 | #if HAVE_GSSAPI |
256 | while ((c = getopt(argc, argv, "ah:j:V:l:P:i:km:p:rsvt:g:p:I:H:T:u:U:w:W:nN?")) != -1) | |
257 | #else | |
5ac5029d | 258 | while ((c = getopt(argc, argv, "ah:j:V:l:P:i:km:p:rsvt:g:p:I:H:T:u:U:w:W:?")) != -1) |
823d23e4 | 259 | #endif |
3afd7aae | 260 | switch (c) { |
62e76326 | 261 | |
3afd7aae AJ |
262 | case 'a': |
263 | opt_noaccept = 1; | |
264 | break; | |
62e76326 | 265 | |
3afd7aae | 266 | case 'h': /* remote host */ |
3afd7aae AJ |
267 | if (optarg != NULL) |
268 | hostname = optarg; | |
5ac5029d | 269 | break; |
62e76326 | 270 | |
5ac5029d | 271 | case 'j': |
af6a12ee AJ |
272 | host = optarg; |
273 | break; | |
5ac5029d AJ |
274 | |
275 | case 'V': | |
276 | if (optarg != NULL) | |
277 | version = optarg; | |
3afd7aae | 278 | break; |
62e76326 | 279 | |
3afd7aae AJ |
280 | case 'l': /* local host */ |
281 | if (optarg != NULL) | |
282 | localhost = optarg; | |
3afd7aae | 283 | break; |
62e76326 | 284 | |
3afd7aae AJ |
285 | case 's': /* silent */ |
286 | to_stdout = 0; | |
3afd7aae | 287 | break; |
62e76326 | 288 | |
3afd7aae AJ |
289 | case 'k': /* backward compat */ |
290 | keep_alive = 1; | |
3afd7aae | 291 | break; |
62e76326 | 292 | |
3afd7aae AJ |
293 | case 'r': /* reload */ |
294 | reload = 1; | |
3afd7aae | 295 | break; |
62e76326 | 296 | |
3afd7aae AJ |
297 | case 'p': /* port number */ |
298 | sscanf(optarg, "%d", &port); | |
3afd7aae AJ |
299 | if (port < 1) |
300 | port = CACHE_HTTP_PORT; /* default */ | |
3afd7aae | 301 | break; |
62e76326 | 302 | |
3afd7aae AJ |
303 | case 'P': |
304 | put_file = xstrdup(optarg); | |
3afd7aae | 305 | break; |
62e76326 | 306 | |
3afd7aae AJ |
307 | case 'i': /* IMS */ |
308 | ims = (time_t) atoi(optarg); | |
3afd7aae | 309 | break; |
62e76326 | 310 | |
3afd7aae AJ |
311 | case 'm': |
312 | method = xstrdup(optarg); | |
3afd7aae | 313 | break; |
62e76326 | 314 | |
3afd7aae AJ |
315 | case 't': |
316 | method = xstrdup("TRACE"); | |
3afd7aae | 317 | max_forwards = atoi(optarg); |
3afd7aae | 318 | break; |
62e76326 | 319 | |
3afd7aae AJ |
320 | case 'g': |
321 | ping = 1; | |
3afd7aae | 322 | pcount = atoi(optarg); |
3afd7aae | 323 | to_stdout = 0; |
3afd7aae | 324 | break; |
62e76326 | 325 | |
3afd7aae AJ |
326 | case 'I': |
327 | if ((ping_int = atoi(optarg) * 1000) <= 0) | |
328 | usage(argv[0]); | |
3afd7aae | 329 | break; |
62e76326 | 330 | |
3afd7aae AJ |
331 | case 'H': |
332 | if (strlen(optarg)) { | |
333 | char *t; | |
334 | strncpy(extra_hdrs, optarg, sizeof(extra_hdrs)); | |
3afd7aae AJ |
335 | while ((t = strstr(extra_hdrs, "\\n"))) |
336 | *t = '\r', *(t + 1) = '\n'; | |
337 | } | |
338 | break; | |
62e76326 | 339 | |
3afd7aae AJ |
340 | case 'T': |
341 | io_timeout = atoi(optarg); | |
342 | break; | |
62e76326 | 343 | |
3afd7aae AJ |
344 | case 'u': |
345 | proxy_user = optarg; | |
346 | break; | |
62e76326 | 347 | |
3afd7aae AJ |
348 | case 'w': |
349 | proxy_password = optarg; | |
350 | break; | |
62e76326 | 351 | |
3afd7aae AJ |
352 | case 'U': |
353 | www_user = optarg; | |
354 | break; | |
62e76326 | 355 | |
3afd7aae AJ |
356 | case 'W': |
357 | www_password = optarg; | |
358 | break; | |
62e76326 | 359 | |
823d23e4 AJ |
360 | #if HAVE_GSSAPI |
361 | case 'n': | |
362 | proxy_neg = 1; | |
363 | break; | |
364 | ||
365 | case 'N': | |
366 | www_neg = 1; | |
367 | break; | |
368 | #endif | |
3afd7aae AJ |
369 | case 'v': |
370 | /* undocumented: may increase verb-level by giving more -v's */ | |
371 | opt_verbose++; | |
372 | break; | |
62e76326 | 373 | |
3afd7aae | 374 | case '?': /* usage */ |
62e76326 | 375 | |
3afd7aae AJ |
376 | default: |
377 | usage(argv[0]); | |
378 | break; | |
379 | } | |
090089c4 | 380 | } |
0ef0f1de | 381 | #ifdef _SQUID_MSWIN_ |
382 | { | |
3afd7aae AJ |
383 | WSADATA wsaData; |
384 | WSAStartup(2, &wsaData); | |
385 | atexit(Win32SockCleanup); | |
0ef0f1de | 386 | } |
387 | #endif | |
090089c4 | 388 | /* Build the HTTP request */ |
8a9b6b94 | 389 | if (strncmp(url, "mgr:", 4) == 0) { |
3afd7aae AJ |
390 | char *t = xstrdup(url + 4); |
391 | snprintf(url, BUFSIZ, "cache_object://%s/%s", hostname, t); | |
392 | xfree(t); | |
8a9b6b94 | 393 | } |
cca89eeb | 394 | if (put_file) { |
3afd7aae AJ |
395 | put_fd = open(put_file, O_RDONLY); |
396 | set_our_signal(); | |
397 | ||
398 | if (put_fd < 0) { | |
399 | fprintf(stderr, "%s: can't open file (%s)\n", argv[0], | |
400 | xstrerror()); | |
401 | exit(-1); | |
402 | } | |
ec4daaa5 | 403 | #ifdef _SQUID_WIN32_ |
3afd7aae | 404 | setmode(put_fd, O_BINARY); |
62e76326 | 405 | |
c4aefe96 | 406 | #endif |
62e76326 | 407 | |
3afd7aae | 408 | fstat(put_fd, &sb); |
cca89eeb | 409 | } |
5ac5029d AJ |
410 | |
411 | if (!host) { | |
af6a12ee AJ |
412 | char *newhost = strstr(url, "://"); |
413 | if (newhost) { | |
414 | char *t; | |
415 | newhost += 3; | |
dc47f531 | 416 | newhost = xstrdup(newhost); |
af6a12ee AJ |
417 | t = newhost + strcspn(newhost, "@/?"); |
418 | if (*t == '@') { | |
419 | newhost = t + 1; | |
420 | t = newhost + strcspn(newhost, "@/?"); | |
421 | } | |
422 | *t = '\0'; | |
423 | host = newhost; | |
424 | } | |
5ac5029d AJ |
425 | } |
426 | ||
af6a12ee AJ |
427 | if (version[0] == '-' || !version[0] || version[0] == '0') { |
428 | /* HTTP/0.9, no headers, no version */ | |
5ac5029d AJ |
429 | snprintf(msg, BUFSIZ, "%s %s\r\n", method, url); |
430 | } else { | |
431 | snprintf(msg, BUFSIZ, "%s %s HTTP/%s\r\n", method, url, version); | |
62e76326 | 432 | |
af6a12ee AJ |
433 | if (host) { |
434 | snprintf(buf, BUFSIZ, "Host: %s\r\n", host); | |
435 | strcat(msg,buf); | |
436 | } | |
437 | ||
438 | if (reload) { | |
439 | snprintf(buf, BUFSIZ, "Pragma: no-cache\r\n"); | |
440 | strcat(msg, buf); | |
441 | } | |
442 | if (put_fd > 0) { | |
173bc2bf | 443 | snprintf(buf, BUFSIZ, "Content-length: %" PRId64 "\r\n", (int64_t) sb.st_size); |
af6a12ee AJ |
444 | strcat(msg, buf); |
445 | } | |
446 | if (opt_noaccept == 0) { | |
447 | snprintf(buf, BUFSIZ, "Accept: */*\r\n"); | |
448 | strcat(msg, buf); | |
449 | } | |
450 | if (ims) { | |
451 | snprintf(buf, BUFSIZ, "If-Modified-Since: %s\r\n", mkrfc1123(ims)); | |
452 | strcat(msg, buf); | |
453 | } | |
454 | if (max_forwards > -1) { | |
455 | snprintf(buf, BUFSIZ, "Max-Forwards: %d\r\n", max_forwards); | |
456 | strcat(msg, buf); | |
457 | } | |
458 | if (proxy_user) { | |
459 | const char *user = proxy_user; | |
460 | const char *password = proxy_password; | |
230c091c | 461 | #if HAVE_GETPASS |
af6a12ee AJ |
462 | if (!password) |
463 | password = getpass("Proxy password: "); | |
230c091c | 464 | #endif |
af6a12ee AJ |
465 | if (!password) { |
466 | fprintf(stderr, "ERROR: Proxy password missing\n"); | |
467 | exit(1); | |
468 | } | |
469 | snprintf(buf, BUFSIZ, "%s:%s", user, password); | |
470 | snprintf(buf, BUFSIZ, "Proxy-Authorization: Basic %s\r\n", base64_encode(buf)); | |
471 | strcat(msg, buf); | |
472 | } | |
473 | if (www_user) { | |
474 | const char *user = www_user; | |
475 | const char *password = www_password; | |
230c091c | 476 | #if HAVE_GETPASS |
af6a12ee AJ |
477 | if (!password) |
478 | password = getpass("WWW password: "); | |
230c091c | 479 | #endif |
af6a12ee AJ |
480 | if (!password) { |
481 | fprintf(stderr, "ERROR: WWW password missing\n"); | |
482 | exit(1); | |
483 | } | |
484 | snprintf(buf, BUFSIZ, "%s:%s", user, password); | |
485 | snprintf(buf, BUFSIZ, "Authorization: Basic %s\r\n", base64_encode(buf)); | |
486 | strcat(msg, buf); | |
487 | } | |
823d23e4 AJ |
488 | #if HAVE_GSSAPI |
489 | if (www_neg) { | |
490 | if (host) { | |
491 | snprintf(buf, BUFSIZ, "Authorization: Negotiate %s\r\n", GSSAPI_token(host)); | |
492 | strcat(msg, buf); | |
493 | } else | |
494 | fprintf(stderr, "ERROR: server host missing\n"); | |
495 | } | |
496 | if (proxy_neg) { | |
497 | if (hostname) { | |
498 | snprintf(buf, BUFSIZ, "Proxy-Authorization: Negotiate %s\r\n", GSSAPI_token(hostname)); | |
499 | strcat(msg, buf); | |
500 | } else | |
501 | fprintf(stderr, "ERROR: proxy server host missing\n"); | |
502 | } | |
503 | #endif | |
af6a12ee | 504 | |
95e78500 AJ |
505 | /* HTTP/1.0 may need keep-alive explicitly */ |
506 | if (strcmp(version, "1.0") == 0 && keep_alive) | |
507 | strcat(msg, "Connection: keep-alive\r\n"); | |
508 | ||
509 | /* HTTP/1.1 may need close explicitly */ | |
51d94d10 AJ |
510 | if (!keep_alive) |
511 | strcat(msg, "Connection: close\r\n"); | |
af6a12ee AJ |
512 | |
513 | strcat(msg, extra_hdrs); | |
514 | strcat(msg, "\r\n"); | |
a78886fc | 515 | } |
5ac5029d | 516 | |
63259c34 | 517 | if (opt_verbose) |
5d9acded | 518 | fprintf(stderr, "Request:'%s'\n", msg); |
63259c34 | 519 | |
899bab3f | 520 | if (ping) { |
521 | #if HAVE_SIGACTION | |
62e76326 | 522 | |
3afd7aae | 523 | struct sigaction sa, osa; |
62e76326 | 524 | |
3afd7aae AJ |
525 | if (sigaction(SIGINT, NULL, &osa) == 0 && osa.sa_handler == SIG_DFL) { |
526 | sa.sa_handler = catchSignal; | |
527 | sa.sa_flags = 0; | |
528 | sigemptyset(&sa.sa_mask); | |
529 | (void) sigaction(SIGINT, &sa, NULL); | |
530 | } | |
899bab3f | 531 | #else |
3afd7aae | 532 | void (*osig) (int); |
62e76326 | 533 | |
3afd7aae AJ |
534 | if ((osig = signal(SIGINT, catchSignal)) != SIG_DFL) |
535 | (void) signal(SIGINT, osig); | |
62e76326 | 536 | |
899bab3f | 537 | #endif |
62e76326 | 538 | |
899bab3f | 539 | } |
540 | loops = ping ? pcount : 1; | |
62e76326 | 541 | |
899bab3f | 542 | for (i = 0; loops == 0 || i < loops; i++) { |
3afd7aae | 543 | int fsize = 0; |
cc192b50 | 544 | struct addrinfo *AI = NULL; |
545 | ||
5d9acded AJ |
546 | if (opt_verbose) |
547 | fprintf(stderr, "Resolving... %s\n", hostname); | |
548 | ||
3afd7aae | 549 | /* Connect to the server */ |
988e90e1 | 550 | |
3afd7aae AJ |
551 | if (localhost) { |
552 | if ( !iaddr.GetHostByName(localhost) ) { | |
cc192b50 | 553 | fprintf(stderr, "client: ERROR: Cannot resolve %s: Host unknown.\n", localhost); |
554 | exit(1); | |
555 | } | |
3afd7aae | 556 | } else { |
cc192b50 | 557 | /* Process the remote host name to locate the Protocol required |
558 | in case we are being asked to link to another version of squid */ | |
3afd7aae | 559 | if ( !iaddr.GetHostByName(hostname) ) { |
cc192b50 | 560 | fprintf(stderr, "client: ERROR: Cannot resolve %s: Host unknown.\n", hostname); |
561 | exit(1); | |
562 | } | |
563 | } | |
564 | ||
565 | iaddr.GetAddrInfo(AI); | |
566 | if ((conn = socket(AI->ai_family, AI->ai_socktype, 0)) < 0) { | |
567 | perror("client: socket"); | |
568 | iaddr.FreeAddrInfo(AI); | |
569 | exit(1); | |
570 | } | |
571 | iaddr.FreeAddrInfo(AI); | |
572 | ||
573 | if (localhost && client_comm_bind(conn, iaddr) < 0) { | |
574 | perror("client: bind"); | |
575 | exit(1); | |
576 | } | |
577 | ||
578 | iaddr.SetEmpty(); | |
3afd7aae | 579 | if ( !iaddr.GetHostByName(hostname) ) { |
cc192b50 | 580 | fprintf(stderr, "client: ERROR: Cannot resolve %s: Host unknown.\n", hostname); |
581 | exit(1); | |
582 | } | |
583 | ||
584 | iaddr.SetPort(port); | |
585 | ||
5d9acded AJ |
586 | if (opt_verbose) { |
587 | char ipbuf[MAX_IPSTRLEN]; | |
588 | fprintf(stderr, "Connecting... %s(%s)\n", hostname, iaddr.NtoA(ipbuf, MAX_IPSTRLEN)); | |
589 | } | |
590 | ||
cc192b50 | 591 | if (client_comm_connect(conn, iaddr, ping ? &tv1 : NULL) < 0) { |
e053c141 FC |
592 | char hostnameBuf[MAX_IPSTRLEN]; |
593 | iaddr.ToURL(hostnameBuf, MAX_IPSTRLEN); | |
cc192b50 | 594 | if (errno == 0) { |
e053c141 | 595 | fprintf(stderr, "client: ERROR: Cannot connect to %s: Host unknown.\n", hostnameBuf); |
cc192b50 | 596 | } else { |
597 | char tbuf[BUFSIZ]; | |
e053c141 | 598 | snprintf(tbuf, BUFSIZ, "client: ERROR: Cannot connect to %s", hostnameBuf); |
cc192b50 | 599 | perror(tbuf); |
600 | } | |
601 | exit(1); | |
602 | } | |
5d9acded AJ |
603 | if (opt_verbose) { |
604 | char ipbuf[MAX_IPSTRLEN]; | |
605 | fprintf(stderr, "Connected to: %s (%s)\n", hostname, iaddr.NtoA(ipbuf, MAX_IPSTRLEN)); | |
606 | } | |
988e90e1 | 607 | |
3afd7aae AJ |
608 | /* Send the HTTP request */ |
609 | bytesWritten = mywrite(conn, msg, strlen(msg)); | |
988e90e1 | 610 | |
3afd7aae AJ |
611 | if (bytesWritten < 0) { |
612 | perror("client: ERROR: write"); | |
613 | exit(1); | |
614 | } else if ((unsigned) bytesWritten != strlen(msg)) { | |
615 | fprintf(stderr, "client: ERROR: Cannot send request?: %s\n", msg); | |
616 | exit(1); | |
617 | } | |
cc192b50 | 618 | |
3afd7aae AJ |
619 | if (put_file) { |
620 | int x; | |
621 | lseek(put_fd, 0, SEEK_SET); | |
0ef0f1de | 622 | #ifdef _SQUID_MSWIN_ |
62e76326 | 623 | |
3afd7aae | 624 | while ((x = read(put_fd, buf, sizeof(buf))) > 0) { |
0ef0f1de | 625 | #else |
62e76326 | 626 | |
3afd7aae | 627 | while ((x = myread(put_fd, buf, sizeof(buf))) > 0) { |
0ef0f1de | 628 | #endif |
3afd7aae | 629 | x = mywrite(conn, buf, x); |
62e76326 | 630 | |
3afd7aae | 631 | total_bytes += x; |
62e76326 | 632 | |
3afd7aae AJ |
633 | if (x <= 0) |
634 | break; | |
635 | } | |
62e76326 | 636 | |
3afd7aae AJ |
637 | if (x != 0) |
638 | fprintf(stderr, "client: ERROR: Cannot send file.\n"); | |
639 | } | |
640 | /* Read the data */ | |
54220df8 | 641 | |
0ef0f1de | 642 | #ifdef _SQUID_MSWIN_ |
3afd7aae | 643 | setmode(1, O_BINARY); |
62e76326 | 644 | |
00f768c1 | 645 | #endif |
62e76326 | 646 | |
3afd7aae AJ |
647 | while ((len = myread(conn, buf, sizeof(buf))) > 0) { |
648 | fsize += len; | |
62e76326 | 649 | |
8fee788b AR |
650 | if (to_stdout && fwrite(buf, len, 1, stdout) != 1) |
651 | perror("client: ERROR writing to stdout"); | |
3afd7aae | 652 | } |
62e76326 | 653 | |
0ef0f1de | 654 | #ifdef _SQUID_MSWIN_ |
3afd7aae | 655 | setmode(1, O_TEXT); |
62e76326 | 656 | |
0ef0f1de | 657 | #endif |
62e76326 | 658 | |
3afd7aae | 659 | (void) close(conn); /* done with socket */ |
62e76326 | 660 | |
3afd7aae AJ |
661 | if (interrupted) |
662 | break; | |
62e76326 | 663 | |
3afd7aae | 664 | if (ping) { |
62e76326 | 665 | |
3afd7aae AJ |
666 | struct tm *tmp; |
667 | time_t t2s; | |
668 | long elapsed_msec; | |
62e76326 | 669 | |
3afd7aae AJ |
670 | (void) Now(&tv2); |
671 | elapsed_msec = tvSubMsec(tv1, tv2); | |
672 | t2s = tv2.tv_sec; | |
673 | tmp = localtime(&t2s); | |
674 | fprintf(stderr, "%d-%02d-%02d %02d:%02d:%02d [%d]: %ld.%03ld secs, %f KB/s\n", | |
675 | tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, | |
676 | tmp->tm_hour, tmp->tm_min, tmp->tm_sec, i + 1, | |
677 | elapsed_msec / 1000, elapsed_msec % 1000, | |
678 | elapsed_msec ? (double) fsize / elapsed_msec : -1.0); | |
62e76326 | 679 | |
3afd7aae AJ |
680 | if (i == 0 || elapsed_msec < ping_min) |
681 | ping_min = elapsed_msec; | |
62e76326 | 682 | |
3afd7aae AJ |
683 | if (i == 0 || elapsed_msec > ping_max) |
684 | ping_max = elapsed_msec; | |
62e76326 | 685 | |
3afd7aae | 686 | ping_sum += elapsed_msec; |
62e76326 | 687 | |
3afd7aae AJ |
688 | /* Delay until next "ping_int" boundary */ |
689 | if ((loops == 0 || i + 1 < loops) && elapsed_msec < ping_int) { | |
62e76326 | 690 | |
3afd7aae AJ |
691 | struct timeval tvs; |
692 | long msec_left = ping_int - elapsed_msec; | |
62e76326 | 693 | |
3afd7aae AJ |
694 | tvs.tv_sec = msec_left / 1000; |
695 | tvs.tv_usec = (msec_left % 1000) * 1000; | |
696 | select(0, NULL, NULL, NULL, &tvs); | |
697 | } | |
698 | } | |
090089c4 | 699 | } |
899bab3f | 700 | |
701 | if (ping && i) { | |
3afd7aae AJ |
702 | ping_mean = ping_sum / i; |
703 | fprintf(stderr, "%d requests, round-trip (secs) min/avg/max = " | |
704 | "%ld.%03ld/%ld.%03ld/%ld.%03ld\n", i, | |
705 | ping_min / 1000, ping_min % 1000, ping_mean / 1000, ping_mean % 1000, | |
706 | ping_max / 1000, ping_max % 1000); | |
090089c4 | 707 | } |
090089c4 | 708 | exit(0); |
709 | /*NOTREACHED */ | |
983061ed | 710 | return 0; |
090089c4 | 711 | } |
712 | ||
2c08acd9 | 713 | static int |
b7ac5457 | 714 | client_comm_bind(int sock, const Ip::Address &addr) |
e1381638 | 715 | { |
62e76326 | 716 | |
cc192b50 | 717 | int res; |
62e76326 | 718 | |
cc192b50 | 719 | static struct addrinfo *AI = NULL; |
2c08acd9 | 720 | |
721 | /* Set up the source socket address from which to send. */ | |
62e76326 | 722 | |
cc192b50 | 723 | addr.GetAddrInfo(AI); |
2c08acd9 | 724 | |
cc192b50 | 725 | res = bind(sock, AI->ai_addr, AI->ai_addrlen); |
726 | ||
727 | addr.FreeAddrInfo(AI); | |
728 | ||
729 | return res; | |
2c08acd9 | 730 | } |
731 | ||
b8d8561b | 732 | static int |
b7ac5457 | 733 | client_comm_connect(int sock, const Ip::Address &addr, struct timeval *tvp) |
e1381638 | 734 | { |
cc192b50 | 735 | int res; |
736 | static struct addrinfo *AI = NULL; | |
62e76326 | 737 | |
cc192b50 | 738 | /* Set up the destination socket address for message to send to. */ |
62e76326 | 739 | |
cc192b50 | 740 | addr.GetAddrInfo(AI); |
090089c4 | 741 | |
cc192b50 | 742 | res = connect(sock, AI->ai_addr, AI->ai_addrlen); |
090089c4 | 743 | |
cc192b50 | 744 | addr.FreeAddrInfo(AI); |
62e76326 | 745 | |
899bab3f | 746 | if (tvp) |
cc192b50 | 747 | (void) Now(tvp); |
62e76326 | 748 | |
cc192b50 | 749 | return res; |
090089c4 | 750 | } |
899bab3f | 751 | |
752 | static int | |
e1381638 AJ |
753 | Now(struct timeval *tp) |
754 | { | |
899bab3f | 755 | #if GETTIMEOFDAY_NO_TZP |
756 | return gettimeofday(tp); | |
757 | #else | |
62e76326 | 758 | |
899bab3f | 759 | return gettimeofday(tp, NULL); |
760 | #endif | |
761 | } /* ARGSUSED */ | |
762 | ||
daacd51f | 763 | void |
e1381638 AJ |
764 | catchSignal(int sig) |
765 | { | |
899bab3f | 766 | interrupted = 1; |
767 | fprintf(stderr, "Interrupted.\n"); | |
768 | } | |
b6c6bcef | 769 | |
daacd51f | 770 | void |
e1381638 AJ |
771 | pipe_handler(int sig) |
772 | { | |
b6c6bcef | 773 | fprintf(stderr, "SIGPIPE received.\n"); |
54220df8 | 774 | } |
775 | ||
776 | static void | |
e1381638 AJ |
777 | set_our_signal(void) |
778 | { | |
54220df8 | 779 | #if HAVE_SIGACTION |
62e76326 | 780 | |
54220df8 | 781 | struct sigaction sa; |
782 | sa.sa_handler = pipe_handler; | |
783 | sa.sa_flags = SA_RESTART; | |
784 | sigemptyset(&sa.sa_mask); | |
62e76326 | 785 | |
54220df8 | 786 | if (sigaction(SIGPIPE, &sa, NULL) < 0) { |
3afd7aae AJ |
787 | fprintf(stderr, "Cannot set PIPE signal.\n"); |
788 | exit(-1); | |
54220df8 | 789 | } |
790 | #else | |
791 | signal(SIGPIPE, pipe_handler); | |
62e76326 | 792 | |
54220df8 | 793 | #endif |
794 | ||
795 | } | |
20cbfe5a | 796 | |
797 | static ssize_t | |
e1381638 AJ |
798 | myread(int fd, void *buf, size_t len) |
799 | { | |
00f768c1 | 800 | #ifndef _SQUID_MSWIN_ |
20cbfe5a | 801 | alarm(io_timeout); |
802 | return read(fd, buf, len); | |
00f768c1 | 803 | #else |
804 | ||
805 | return recv(fd, buf, len, 0); | |
806 | #endif | |
20cbfe5a | 807 | } |
808 | ||
809 | static ssize_t | |
e1381638 AJ |
810 | mywrite(int fd, void *buf, size_t len) |
811 | { | |
00f768c1 | 812 | #ifndef _SQUID_MSWIN_ |
20cbfe5a | 813 | alarm(io_timeout); |
814 | return write(fd, buf, len); | |
00f768c1 | 815 | #else |
62e76326 | 816 | |
00f768c1 | 817 | return send(fd, buf, len, 0); |
0ef0f1de | 818 | #endif |
00f768c1 | 819 | } |
823d23e4 AJ |
820 | |
821 | #if HAVE_GSSAPI | |
822 | /* | |
823 | * Check return valuse major_status, minor_status for error and print error description | |
824 | * in case of an error. | |
825 | * Returns 1 in case of gssapi error | |
826 | * 0 in case of no gssapi error | |
827 | */ | |
828 | #define BUFFER_SIZE 8192 | |
829 | static int | |
830 | check_gss_err(OM_uint32 major_status, OM_uint32 minor_status, const char *function) | |
831 | { | |
832 | if (GSS_ERROR(major_status)) { | |
833 | OM_uint32 maj_stat, min_stat; | |
834 | OM_uint32 msg_ctx = 0; | |
835 | gss_buffer_desc status_string; | |
836 | char buf[BUFFER_SIZE]; | |
837 | size_t len; | |
838 | ||
839 | len = 0; | |
840 | msg_ctx = 0; | |
841 | while (!msg_ctx) { | |
842 | /* convert major status code (GSS-API error) to text */ | |
843 | maj_stat = gss_display_status(&min_stat, major_status, | |
844 | GSS_C_GSS_CODE, | |
845 | GSS_C_NULL_OID, | |
846 | &msg_ctx, &status_string); | |
847 | if (maj_stat == GSS_S_COMPLETE) { | |
848 | snprintf(buf + len, BUFFER_SIZE-len, "%s", (char *) status_string.value); | |
849 | len += status_string.length; | |
850 | gss_release_buffer(&min_stat, &status_string); | |
851 | break; | |
852 | } | |
853 | gss_release_buffer(&min_stat, &status_string); | |
854 | } | |
855 | snprintf(buf + len, BUFFER_SIZE-len, "%s", ". "); | |
856 | len += 2; | |
857 | msg_ctx = 0; | |
858 | while (!msg_ctx) { | |
859 | /* convert minor status code (underlying routine error) to text */ | |
860 | maj_stat = gss_display_status(&min_stat, minor_status, | |
861 | GSS_C_MECH_CODE, | |
862 | GSS_C_NULL_OID, | |
863 | &msg_ctx, &status_string); | |
864 | if (maj_stat == GSS_S_COMPLETE) { | |
865 | snprintf(buf + len, BUFFER_SIZE-len,"%s", (char *) status_string.value); | |
866 | len += status_string.length; | |
867 | gss_release_buffer(&min_stat, &status_string); | |
868 | break; | |
869 | } | |
870 | gss_release_buffer(&min_stat, &status_string); | |
871 | } | |
872 | fprintf(stderr, "%s failed: %s\n", function, buf); | |
873 | return (1); | |
874 | } | |
875 | return (0); | |
876 | } | |
877 | ||
878 | /* | |
879 | * Get gssapi token for service HTTP/<server> | |
880 | * User has to initiate a kinit user@DOMAIN on commandline first for the | |
881 | * function to be successful | |
882 | * Returns base64 encoded token if successful | |
883 | * string "ERROR" if unsuccessful | |
884 | */ | |
885 | static char * | |
886 | GSSAPI_token(const char *server) | |
887 | { | |
888 | OM_uint32 major_status, minor_status; | |
889 | gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; | |
890 | gss_name_t server_name = GSS_C_NO_NAME; | |
891 | gss_buffer_desc service = GSS_C_EMPTY_BUFFER; | |
892 | gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; | |
893 | gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; | |
894 | char *token = NULL; | |
895 | ||
896 | setbuf(stdout, NULL); | |
897 | setbuf(stdin, NULL); | |
898 | ||
899 | if (!server) { | |
900 | fprintf(stderr, "Error: No server name\n"); | |
901 | return (char *)"ERROR"; | |
902 | } | |
903 | service.value = xmalloc(strlen("HTTP") + strlen(server) + 2); | |
904 | snprintf((char *) service.value, strlen("HTTP") + strlen(server) + 2, "%s@%s", "HTTP", server); | |
905 | service.length = strlen((char *) service.value); | |
906 | ||
907 | major_status = gss_import_name(&minor_status, &service, | |
908 | gss_nt_service_name, &server_name); | |
909 | ||
910 | if (!check_gss_err(major_status, minor_status, "gss_import_name()")) { | |
911 | ||
912 | major_status = gss_init_sec_context(&minor_status, | |
913 | GSS_C_NO_CREDENTIAL, | |
914 | &gss_context, | |
915 | server_name, | |
916 | gss_mech_spnego, | |
917 | 0, | |
918 | 0, | |
919 | GSS_C_NO_CHANNEL_BINDINGS, | |
920 | &input_token, | |
921 | NULL, | |
922 | &output_token, | |
923 | NULL, | |
924 | NULL); | |
925 | ||
926 | if (!check_gss_err(major_status, minor_status, "gss_init_sec_context()")) { | |
927 | ||
928 | if (output_token.length) | |
929 | token = (char *) base64_encode_bin((const char *) output_token.value, output_token.length); | |
930 | } | |
931 | } | |
932 | ||
933 | if (!output_token.length) | |
934 | token = (char *) "ERROR"; | |
935 | gss_delete_sec_context(&minor_status, &gss_context, NULL); | |
936 | gss_release_buffer(&minor_status, &service); | |
937 | gss_release_buffer(&minor_status, &input_token); | |
938 | gss_release_buffer(&minor_status, &output_token); | |
939 | gss_release_name(&minor_status, &server_name); | |
940 | ||
941 | return token; | |
942 | } | |
943 | #endif |