]> git.ipfire.org Git - thirdparty/squid.git/blame - tools/cachemgr.cc
Bug 2383: part 2: Fix IPv6 --with-* option testing
[thirdparty/squid.git] / tools / cachemgr.cc
CommitLineData
30a4f2a8 1/*
cc192b50 2 * $Id: cachemgr.cc,v 1.6 2007/12/14 23:11:53 amosjeffries Exp $
30a4f2a8 3 *
f43e2ec2 4 * DEBUG: section 0 CGI Cache Manager
d23f4e1b 5 * AUTHOR: Duane Wessels
30a4f2a8 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.
e25c139f 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.
e25c139f 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.
d23f4e1b 32 *
30a4f2a8 33 */
234967c9 34
35#include "config.h"
234967c9 36
30a4f2a8 37#if HAVE_UNISTD_H
234967c9 38#include <unistd.h>
30a4f2a8 39#endif
40#if HAVE_STDLIB_H
234967c9 41#include <stdlib.h>
30a4f2a8 42#endif
43#if HAVE_STDIO_H
234967c9 44#include <stdio.h>
30a4f2a8 45#endif
46#if HAVE_SYS_TYPES_H
234967c9 47#include <sys/types.h>
30a4f2a8 48#endif
49#if HAVE_CTYPE_H
234967c9 50#include <ctype.h>
30a4f2a8 51#endif
52#if HAVE_ERRNO_H
234967c9 53#include <errno.h>
30a4f2a8 54#endif
55#if HAVE_FCNTL_H
234967c9 56#include <fcntl.h>
30a4f2a8 57#endif
58#if HAVE_GRP_H
234967c9 59#include <grp.h>
30a4f2a8 60#endif
88738790 61#if HAVE_GNUMALLOC_H
62#include <gnumalloc.h>
482aa790 63#elif HAVE_MALLOC_H
234967c9 64#include <malloc.h>
983061ed 65#endif
30a4f2a8 66#if HAVE_MEMORY_H
234967c9 67#include <memory.h>
30a4f2a8 68#endif
69#if HAVE_NETDB_H && !defined(_SQUID_NETDB_H_) /* protect NEXTSTEP */
70#define _SQUID_NETDB_H_
234967c9 71#include <netdb.h>
30a4f2a8 72#endif
73#if HAVE_PWD_H
234967c9 74#include <pwd.h>
30a4f2a8 75#endif
76#if HAVE_SIGNAL_H
234967c9 77#include <signal.h>
30a4f2a8 78#endif
79#if HAVE_TIME_H
234967c9 80#include <time.h>
30a4f2a8 81#endif
82#if HAVE_SYS_PARAM_H
234967c9 83#include <sys/param.h>
30a4f2a8 84#endif
85#if HAVE_SYS_TIME_H
234967c9 86#include <sys/time.h>
30a4f2a8 87#endif
88#if HAVE_SYS_RESOURCE_H
234967c9 89#include <sys/resource.h> /* needs sys/time.h above it */
30a4f2a8 90#endif
91#if HAVE_SYS_SOCKET_H
234967c9 92#include <sys/socket.h>
30a4f2a8 93#endif
94#if HAVE_NETINET_IN_H
234967c9 95#include <netinet/in.h>
30a4f2a8 96#endif
97#if HAVE_ARPA_INET_H
234967c9 98#include <arpa/inet.h>
30a4f2a8 99#endif
100#if HAVE_SYS_STAT_H
234967c9 101#include <sys/stat.h>
30a4f2a8 102#endif
103#if HAVE_SYS_UN_H
234967c9 104#include <sys/un.h>
30a4f2a8 105#endif
106#if HAVE_SYS_WAIT_H
234967c9 107#include <sys/wait.h>
30a4f2a8 108#endif
109#if HAVE_LIBC_H
110#include <libc.h>
111#endif
112#if HAVE_STRING_H
234967c9 113#include <string.h>
114#endif
30a4f2a8 115#if HAVE_STRINGS_H
234967c9 116#include <strings.h>
117#endif
234967c9 118#if HAVE_BSTRING_H
119#include <bstring.h>
120#endif
30a4f2a8 121#if HAVE_CRYPT_H
234967c9 122#include <crypt.h>
123#endif
234967c9 124#if HAVE_SYS_SELECT_H
125#include <sys/select.h>
126#endif
42ad37af 127#if HAVE_FNMATCH_H
7dcff512 128extern "C"
129{
42ad37af 130#include <fnmatch.h>
7dcff512 131}
42ad37af 132#endif
090089c4 133
6a4a7312 134#include "assert.h"
473471f2 135#include "util.h"
cc192b50 136#include "IPAddress.h"
e1f7507e 137#include "getfullhostname.h"
090089c4 138
42ad37af 139#ifndef DEFAULT_CACHEMGR_CONFIG
b4ee1908 140#define DEFAULT_CACHEMGR_CONFIG "/etc/squid/cachemgr.conf"
42ad37af 141#endif
142
62e76326 143typedef struct
144{
42ad37af 145 char *server;
d23f4e1b 146 char *hostname;
147 int port;
148 char *action;
7395afb8 149 char *user_name;
d23f4e1b 150 char *passwd;
7395afb8 151 char *pub_auth;
2fadd50d 152} cachemgr_request;
090089c4 153
d23f4e1b 154/*
7395afb8 155 * Debugging macros (info goes to error_log on your web server)
156 * Note: do not run cache manager with non zero debugging level
157 * if you do not debug, it may write a lot of [sensitive]
158 * information to your error log.
d23f4e1b 159 */
7395afb8 160
161/* debugging level 0 (disabled) - 3 (max) */
162#define DEBUG_LEVEL 0
b9bc2838 163#define debug(level) if ((level) <= DEBUG_LEVEL && DEBUG_LEVEL > 0)
7395afb8 164
165/*
166 * Static variables and constants
167 */
2ac76861 168static const time_t passwd_ttl = 60 * 60 * 3; /* in sec */
0ee4272b 169static const char *script_name = "/cgi-bin/cachemgr.cgi";
0ee4272b 170static const char *progname = NULL;
d1a43e28 171static time_t now;
62e76326 172
d23f4e1b 173/*
174 * Function prototypes
175 */
4055bb7e 176#define safe_free(str) { if (str) { xfree(str); (str) = NULL; } }
7395afb8 177static const char *safe_str(const char *str);
a2c963ae 178static const char *xstrtok(char **str, char del);
f5b8bbc4 179static void print_trailer(void);
a2c963ae 180static void auth_html(const char *host, int port, const char *user_name);
d23f4e1b 181static void error_html(const char *msg);
7395afb8 182static char *menu_url(cachemgr_request * req, const char *action);
183static int parse_status_line(const char *sline, const char **statusStr);
d23f4e1b 184static cachemgr_request *read_request(void);
185static char *read_get_request(void);
186static char *read_post_request(void);
187
2ac76861 188static void make_pub_auth(cachemgr_request * req);
189static void decode_pub_auth(cachemgr_request * req);
190static void reset_auth(cachemgr_request * req);
191static const char *make_auth_header(const cachemgr_request * req);
7395afb8 192
42ad37af 193static int check_target_acl(const char *hostname, int port);
194
099a1791 195#ifdef _SQUID_MSWIN_
196static int s_iInitCount = 0;
a760862a 197
099a1791 198int Win32SockInit(void)
199{
200 int iVersionRequested;
201 WSADATA wsaData;
202 int err;
203
204 if (s_iInitCount > 0) {
62e76326 205 s_iInitCount++;
206 return (0);
207 } else if (s_iInitCount < 0)
208 return (s_iInitCount);
099a1791 209
210 /* s_iInitCount == 0. Do the initailization */
211 iVersionRequested = MAKEWORD(2, 0);
62e76326 212
099a1791 213 err = WSAStartup((WORD) iVersionRequested, &wsaData);
62e76326 214
099a1791 215 if (err) {
62e76326 216 s_iInitCount = -1;
217 return (s_iInitCount);
099a1791 218 }
62e76326 219
099a1791 220 if (LOBYTE(wsaData.wVersion) != 2 ||
62e76326 221 HIBYTE(wsaData.wVersion) != 0) {
222 s_iInitCount = -2;
223 WSACleanup();
224 return (s_iInitCount);
099a1791 225 }
62e76326 226
099a1791 227 s_iInitCount++;
228 return (s_iInitCount);
229}
230
231void Win32SockCleanup(void)
232{
233 if (--s_iInitCount == 0)
62e76326 234 WSACleanup();
235
099a1791 236 return;
237}
62e76326 238
099a1791 239#endif /* ifdef _SQUID_MSWIN_ */
7395afb8 240
2ac76861 241static const char *
242safe_str(const char *str)
7395afb8 243{
244 return str ? str : "";
245}
246
7021844c 247/* relaxed number format */
248static int
249is_number(const char *str)
250{
251 return strspn(str, "\t -+01234567890./\n") == strlen(str);
252}
253
a2c963ae 254static const char *
2ac76861 255xstrtok(char **str, char del)
7395afb8 256{
257 if (*str) {
62e76326 258 char *p = strchr(*str, del);
259 char *tok = *str;
260 int len;
261
262 if (p) {
263 *str = p + 1;
264 *p = '\0';
265 } else
266 *str = NULL;
267
268 /* trim */
269 len = strlen(tok);
270
271 while (len && xisspace(tok[len - 1]))
272 tok[--len] = '\0';
273
274 while (xisspace(*tok))
275 tok++;
276
277 return tok;
7395afb8 278 } else
62e76326 279 return "";
7395afb8 280}
090089c4 281
24382924 282static void
8203a132 283print_trailer(void)
090089c4 284{
df339671 285 printf("<HR noshade size=\"1px\">\n");
090089c4 286 printf("<ADDRESS>\n");
287 printf("Generated %s, by %s/%s@%s\n",
62e76326 288 mkrfc1123(now), progname, VERSION, getfullhostname());
a8f7d3ee 289 printf("</ADDRESS></BODY></HTML>\n");
090089c4 290}
291
24382924 292static void
a2c963ae 293auth_html(const char *host, int port, const char *user_name)
090089c4 294{
42ad37af 295 FILE *fp;
296 int need_host = 1;
297
2ac76861 298 if (!user_name)
62e76326 299 user_name = "";
300
2ac76861 301 if (!host || !strlen(host))
42ad37af 302 host = "";
62e76326 303
df339671 304 printf("Content-Type: text/html\r\n\r\n");
62e76326 305
df339671 306 printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
62e76326 307
df339671 308 printf("<HTML><HEAD><TITLE>Cache Manager Interface</TITLE>\n");
62e76326 309
d8c0128e 310 printf("<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE></HEAD>\n");
62e76326 311
a8f7d3ee 312 printf("<BODY><H1>Cache Manager Interface</H1>\n");
62e76326 313
f7d9a2ad 314 printf("<P>This is a WWW interface to the instrumentation interface\n");
62e76326 315
f7d9a2ad 316 printf("for the Squid object cache.</P>\n");
62e76326 317
df339671 318 printf("<HR noshade size=\"1px\">\n");
62e76326 319
f23f96e6 320 printf("<FORM METHOD=\"POST\" ACTION=\"%s\">\n", script_name);
62e76326 321
d980562f 322 printf("<TABLE BORDER=\"0\" CELLPADDING=\"10\" CELLSPACING=\"1\">\n");
62e76326 323
62e76326 324
42ad37af 325 fp = fopen("cachemgr.conf", "r");
326
327 if (fp == NULL)
328 fp = fopen(DEFAULT_CACHEMGR_CONFIG, "r");
329
330 if (fp != NULL) {
331 int servers = 0;
332 char config_line[BUFSIZ];
333
334 while (fgets(config_line, BUFSIZ, fp)) {
335 char *server, *comment;
336 strtok(config_line, "\r\n");
337
338 if (config_line[0] == '#')
339 continue;
340
341 if (config_line[0] == '\0')
342 continue;
343
344 if ((server = strtok(config_line, " \t")) == NULL)
345 continue;
346
347 if (strchr(server, '*') || strchr(server, '[') || strchr(server, '?')) {
348 need_host = -1;
349 continue;
350 }
351
352 comment = strtok(NULL, "");
353
354 if (comment)
355 while (*comment == ' ' || *comment == '\t')
356 comment++;
357
358 if (!comment || !*comment)
359 comment = server;
360
361 if (!servers) {
362 printf("<TR><TH ALIGN=\"left\">Cache Server:</TH><TD><SELECT NAME=\"server\">\n");
363 }
364
365 printf("<OPTION VALUE=\"%s\"%s>%s</OPTION>\n", server, (servers || *host) ? "" : " SELECTED", comment);
366 servers++;
367 }
368
369 if (servers) {
370 if (need_host == 1 && !*host)
371 need_host = 0;
372
373 if (need_host)
374 printf("<OPTION VALUE=\"\"%s>Other</OPTION>\n", (*host) ? " SELECTED" : "");
375
376 printf("</SELECT></TR>\n");
377 }
378
379 fclose(fp);
380 }
381
382 if (need_host) {
383 if (need_host == 1 && !*host)
384 host = "localhost";
385
386 printf("<TR><TH ALIGN=\"left\">Cache Host:</TH><TD><INPUT NAME=\"host\" ");
62e76326 387
42ad37af 388 printf("size=\"30\" VALUE=\"%s\"></TD></TR>\n", host);
62e76326 389
42ad37af 390 printf("<TR><TH ALIGN=\"left\">Cache Port:</TH><TD><INPUT NAME=\"port\" ");
391
392 printf("size=\"30\" VALUE=\"%d\"></TD></TR>\n", port);
393 }
62e76326 394
7395afb8 395 printf("<TR><TH ALIGN=\"left\">Manager name:</TH><TD><INPUT NAME=\"user_name\" ");
62e76326 396
df339671 397 printf("size=\"30\" VALUE=\"%s\"></TD></TR>\n", user_name);
62e76326 398
7395afb8 399 printf("<TR><TH ALIGN=\"left\">Password:</TH><TD><INPUT TYPE=\"password\" NAME=\"passwd\" ");
62e76326 400
df339671 401 printf("size=\"30\" VALUE=\"\"></TD></TR>\n");
62e76326 402
7395afb8 403 printf("</TABLE><BR CLEAR=\"all\">\n");
62e76326 404
d23f4e1b 405 printf("<INPUT TYPE=\"submit\" VALUE=\"Continue...\">\n");
62e76326 406
7395afb8 407 printf("</FORM>\n");
62e76326 408
090089c4 409 print_trailer();
410}
411
d23f4e1b 412static void
413error_html(const char *msg)
090089c4 414{
df339671 415 printf("Content-Type: text/html\r\n\r\n");
416 printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
417 printf("<HTML><HEAD><TITLE>Cache Manager Error</TITLE>\n");
d8c0128e 418 printf("<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE></HEAD>\n");
d23f4e1b 419 printf("<BODY><H1>Cache Manager Error</H1>\n");
420 printf("<P>\n%s</P>\n", msg);
421 print_trailer();
090089c4 422}
423
7395afb8 424/* returns http status extracted from status line or -1 on parsing failure */
425static int
426parse_status_line(const char *sline, const char **statusStr)
427{
428 const char *sp = strchr(sline, ' ');
62e76326 429
7395afb8 430 if (statusStr)
62e76326 431 *statusStr = NULL;
432
7395afb8 433 if (strncasecmp(sline, "HTTP/", 5) || !sp)
62e76326 434 return -1;
435
3d0ac046 436 while (xisspace(*++sp));
b6a2f15e 437 if (!xisdigit(*sp))
62e76326 438 return -1;
439
7395afb8 440 if (statusStr)
62e76326 441 *statusStr = sp;
442
7395afb8 443 return atoi(sp);
444}
445
d23f4e1b 446static char *
7395afb8 447menu_url(cachemgr_request * req, const char *action)
090089c4 448{
d23f4e1b 449 static char url[1024];
137ee196 450 snprintf(url, sizeof(url), "%s?host=%s&port=%d&user_name=%s&operation=%s&auth=%s",
62e76326 451 script_name,
452 req->hostname,
453 req->port,
454 safe_str(req->user_name),
455 action,
456 safe_str(req->pub_auth));
d23f4e1b 457 return url;
090089c4 458}
459
d23f4e1b 460static const char *
461munge_menu_line(const char *buf, cachemgr_request * req)
090089c4 462{
d23f4e1b 463 char *x;
7395afb8 464 const char *a;
465 const char *d;
466 const char *p;
467 char *a_url;
7021844c 468 char *buf_copy;
399e85ea 469 static char html[2 * 1024];
62e76326 470
d23f4e1b 471 if (strlen(buf) < 1)
62e76326 472 return buf;
473
d23f4e1b 474 if (*buf != ' ')
62e76326 475 return buf;
476
7021844c 477 buf_copy = x = xstrdup(buf);
62e76326 478
7395afb8 479 a = xstrtok(&x, '\t');
62e76326 480
7395afb8 481 d = xstrtok(&x, '\t');
62e76326 482
7395afb8 483 p = xstrtok(&x, '\t');
62e76326 484
7395afb8 485 a_url = xstrdup(menu_url(req, a));
62e76326 486
7395afb8 487 /* no reason to give a url for a disabled action */
488 if (!strcmp(p, "disabled"))
62e76326 489 snprintf(html, sizeof(html), "<LI type=\"circle\">%s (disabled)<A HREF=\"%s\">.</A>\n", d, a_url);
7395afb8 490 else
62e76326 491 /* disable a hidden action (requires a password, but password is not in squid.conf) */
492 if (!strcmp(p, "hidden"))
493 snprintf(html, sizeof(html), "<LI type=\"circle\">%s (hidden)<A HREF=\"%s\">.</A>\n", d, a_url);
494 else
495 /* disable link if authentication is required and we have no password */
496 if (!strcmp(p, "protected") && !req->passwd)
497 snprintf(html, sizeof(html), "<LI type=\"circle\">%s (requires <a href=\"%s\">authentication</a>)<A HREF=\"%s\">.</A>\n",
498 d, menu_url(req, "authenticate"), a_url);
499 else
500 /* highlight protected but probably available entries */
501 if (!strcmp(p, "protected"))
502 snprintf(html, sizeof(html), "<LI type=\"square\"><A HREF=\"%s\"><font color=\"#FF0000\">%s</font></A>\n",
503 a_url, d);
504
7395afb8 505 /* public entry or unknown type of protection */
62e76326 506 else
507 snprintf(html, sizeof(html), "<LI type=\"disk\"><A HREF=\"%s\">%s</A>\n", a_url, d);
508
7395afb8 509 xfree(a_url);
62e76326 510
7021844c 511 xfree(buf_copy);
62e76326 512
7021844c 513 return html;
514}
515
516static const char *
517munge_other_line(const char *buf, cachemgr_request * req)
518{
62e76326 519 static const char *ttags[] = {"td", "th"};
520
7021844c 521 static char html[4096];
728da2ee 522 static int table_line_num = 0;
523 static int next_is_header = 0;
7021844c 524 int is_header = 0;
525 const char *ttag;
7021844c 526 char *buf_copy;
b9bc2838 527 char *x, *p;
7021844c 528 int l = 0;
529 /* does it look like a table? */
62e76326 530
7021844c 531 if (!strchr(buf, '\t') || *buf == '\t') {
62e76326 532 /* nope, just text */
533 snprintf(html, sizeof(html), "%s%s",
534 table_line_num ? "</table>\n<pre>" : "", buf);
535 table_line_num = 0;
536 return html;
7021844c 537 }
62e76326 538
b9bc2838 539 /* start html table */
540 if (!table_line_num) {
62e76326 541 l += snprintf(html + l, sizeof(html) - l, "</pre><table cellpadding=\"2\" cellspacing=\"1\">\n");
542 next_is_header = 0;
b9bc2838 543 }
62e76326 544
b9bc2838 545 /* remove '\n' */
546 is_header = (!table_line_num || next_is_header) && !strchr(buf, ':') && !is_number(buf);
62e76326 547
7021844c 548 ttag = ttags[is_header];
62e76326 549
7021844c 550 /* record starts */
399e85ea 551 l += snprintf(html + l, sizeof(html) - l, "<tr>");
62e76326 552
7021844c 553 /* substitute '\t' */
554 buf_copy = x = xstrdup(buf);
62e76326 555
399e85ea 556 if ((p = strchr(x, '\n')))
62e76326 557 *p = '\0';
558
b9bc2838 559 while (x && strlen(x)) {
62e76326 560 int column_span = 1;
561 const char *cell = xstrtok(&x, '\t');
562
563 while (x && *x == '\t') {
564 column_span++;
565 x++;
566 }
567
568 l += snprintf(html + l, sizeof(html) - l, "<%s colspan=\"%d\" align=\"%s\">%s</%s>",
569 ttag, column_span,
570 is_header ? "center" : is_number(cell) ? "right" : "left",
571 cell, ttag);
7021844c 572 }
62e76326 573
7021844c 574 xfree(buf_copy);
575 /* record ends */
399e85ea 576 l += snprintf(html + l, sizeof(html) - l, "</tr>\n");
b9bc2838 577 next_is_header = is_header && strstr(buf, "\t\t");
7021844c 578 table_line_num++;
d23f4e1b 579 return html;
090089c4 580}
581
d23f4e1b 582static int
583read_reply(int s, cachemgr_request * req)
090089c4 584{
2ac76861 585 char buf[4 * 1024];
099a1791 586#ifdef _SQUID_MSWIN_
62e76326 587
099a1791 588 int reply;
a760862a 589 char *tmpfile = tempnam(NULL, "tmp0000");
590 FILE *fp = fopen(tmpfile, "w+");
099a1791 591#else
62e76326 592
d23f4e1b 593 FILE *fp = fdopen(s, "r");
099a1791 594#endif
7395afb8 595 /* interpretation states */
2ac76861 596 enum {
62e76326 597 isStatusLine, isHeaders, isBodyStart, isBody, isForward, isEof, isForwardEof, isSuccess, isError
2ac76861 598 } istate = isStatusLine;
d23f4e1b 599 int parse_menu = 0;
7395afb8 600 const char *action = req->action;
601 const char *statusStr = NULL;
602 int status = -1;
62e76326 603
d23f4e1b 604 if (0 == strlen(req->action))
62e76326 605 parse_menu = 1;
d23f4e1b 606 else if (0 == strcasecmp(req->action, "menu"))
62e76326 607 parse_menu = 1;
608
d23f4e1b 609 if (fp == NULL) {
a760862a 610#ifdef _SQUID_MSWIN_
611 perror(tmpfile);
612 xfree(tmpfile);
613#else
614
62e76326 615 perror("fdopen");
a760862a 616#endif
617
618 close(s);
62e76326 619 return 1;
090089c4 620 }
62e76326 621
099a1791 622#ifdef _SQUID_MSWIN_
ad1fa9c9 623
624 while ((reply=recv(s, buf , sizeof(buf), 0)) > 0)
625 fwrite(buf, 1, reply, fp);
62e76326 626
099a1791 627 rewind(fp);
62e76326 628
099a1791 629#endif
62e76326 630
7395afb8 631 if (parse_menu)
62e76326 632 action = "menu";
633
7395afb8 634 /* read reply interpreting one line at a time depending on state */
635 while (istate < isEof) {
62e76326 636 if (!fgets(buf, sizeof(buf), fp))
637 istate = istate == isForward ? isForwardEof : isEof;
638
639 switch (istate) {
640
641 case isStatusLine:
642 /* get HTTP status */
643 /* uncomment the following if you want to debug headers */
644 /* fputs("\r\n\r\n", stdout); */
645 status = parse_status_line(buf, &statusStr);
646 istate = status == 200 ? isHeaders : isForward;
647 /* if cache asks for authentication, we have to reset our info */
648
649 if (status == 401 || status == 407) {
650 reset_auth(req);
651 status = 403; /* Forbiden, see comments in case isForward: */
652 }
653
654 /* this is a way to pass HTTP status to the Web server */
655 if (statusStr)
656 printf("Status: %d %s", status, statusStr); /* statusStr has '\n' */
657
658 break;
659
660 case isHeaders:
661 /* forward header field */
662 if (!strcmp(buf, "\r\n")) { /* end of headers */
663 fputs("Content-Type: text/html\r\n", stdout); /* add our type */
664 istate = isBodyStart;
665 }
666
667 if (strncasecmp(buf, "Content-Type:", 13)) /* filter out their type */
668 fputs(buf, stdout);
669
670 break;
671
672 case isBodyStart:
673 printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
674
675 printf("<HTML><HEAD><TITLE>CacheMgr@%s: %s</TITLE>\n",
676 req->hostname, action);
677
907e86e0 678 printf("<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}TABLE{background-color:#333333;border:0pt;padding:0pt}TH,TD{background-color:#ffffff;white-space:nowrap}--></STYLE>\n");
62e76326 679
680 printf("</HEAD><BODY>\n");
681
682 if (parse_menu) {
683 printf("<H2><a href=\"%s\">Cache Manager</a> menu for %s:</H2>",
684 menu_url(req, "authenticate"), req->hostname);
685 printf("<UL>\n");
686 } else {
687 printf("<P><A HREF=\"%s\">%s</A>\n<HR noshade size=\"1px\">\n",
688 menu_url(req, "menu"), "Cache Manager menu");
689 printf("<PRE>\n");
690 }
691
692 istate = isBody;
693 /* yes, fall through, we do not want to loose the first line */
694
695 case isBody:
696 /* interpret [and reformat] cache response */
697
698 if (parse_menu)
699 fputs(munge_menu_line(buf, req), stdout);
700 else
701 fputs(munge_other_line(buf, req), stdout);
702
703 break;
704
705 case isForward:
706 /* forward: no modifications allowed */
707 /*
708 * Note: we currently do not know any way to get browser.reply to
709 * 401 to .cgi because web server filters out all auth info. Thus we
710 * disable authentication headers for now.
711 */
712 if (!strncasecmp(buf, "WWW-Authenticate:", 17) || !strncasecmp(buf, "Proxy-Authenticate:", 19))
713
714 ; /* skip */
715 else
716 fputs(buf, stdout);
717
718 break;
719
720 case isEof:
721 /* print trailers */
722 if (parse_menu)
723 printf("</UL>\n");
724 else
725 printf("</table></PRE>\n");
726
727 print_trailer();
728
729 istate = isSuccess;
730
731 break;
732
733 case isForwardEof:
734 /* indicate that we finished processing an "error" sequence */
735 istate = isError;
736
737 break;
738
739 default:
740 printf("%s: internal bug: invalid state reached: %d", script_name, istate);
741
742 istate = isError;
743 }
d23f4e1b 744 }
62e76326 745
ab5fa751 746 fclose(fp);
a760862a 747#ifdef _SQUID_MSWIN_
748
749 remove(tmpfile);
750 xfree(tmpfile);
751 close(s);
752
753#endif
754
d23f4e1b 755 return 0;
d1a43e28 756}
090089c4 757
d23f4e1b 758static int
759process_request(cachemgr_request * req)
090089c4 760{
62e76326 761
cc192b50 762 char ipbuf[MAX_IPSTRLEN];
763 struct addrinfo *AI = NULL;
764 IPAddress S;
d23f4e1b 765 int s;
766 int l;
62e76326 767
2ac76861 768 static char buf[2 * 1024];
62e76326 769
d23f4e1b 770 if (req == NULL) {
62e76326 771 auth_html(CACHEMGR_HOSTNAME, CACHE_HTTP_PORT, "");
772 return 1;
d23f4e1b 773 }
62e76326 774
d23f4e1b 775 if (req->hostname == NULL) {
62e76326 776 req->hostname = xstrdup(CACHEMGR_HOSTNAME);
d23f4e1b 777 }
62e76326 778
d23f4e1b 779 if (req->port == 0) {
62e76326 780 req->port = CACHE_HTTP_PORT;
d23f4e1b 781 }
62e76326 782
d23f4e1b 783 if (req->action == NULL) {
62e76326 784 req->action = xstrdup("");
d23f4e1b 785 }
62e76326 786
42ad37af 787 if (strcmp(req->action, "authenticate") == 0) {
62e76326 788 auth_html(req->hostname, req->port, req->user_name);
789 return 0;
7395afb8 790 }
62e76326 791
42ad37af 792 if (!check_target_acl(req->hostname, req->port)) {
793 snprintf(buf, 1024, "target %s:%d not allowed in cachemgr.conf\n", req->hostname, req->port);
794 error_html(buf);
795 return 1;
796 }
797
cc192b50 798 S = *gethostbyname(req->hostname);
62e76326 799
cc192b50 800 if ( !S.IsAnyAddr() ) {
801 (void) 0;
802 } else if ( S = req->hostname)
62e76326 803 (void) 0;
d23f4e1b 804 else {
62e76326 805 snprintf(buf, 1024, "Unknown host: %s\n", req->hostname);
806 error_html(buf);
807 return 1;
d23f4e1b 808 }
62e76326 809
cc192b50 810 S.SetPort(req->port);
811
812 S.GetAddrInfo(AI);
62e76326 813
616f5a95
AJ
814#if USE_IPV6
815 if ((s = socket( AI->ai_family, SOCK_STREAM, 0)) < 0) {
816#else
817 if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
818#endif
819 snprintf(buf, 1024, "socket: %s\n", xstrerror());
820 error_html(buf);
821 return 1;
822 }
823
cc192b50 824 if (connect(s, AI->ai_addr, AI->ai_addrlen) < 0) {
825 snprintf(buf, 1024, "connect %s: %s\n",
826 S.ToURL(ipbuf,MAX_IPSTRLEN),
023b0d1d 827 xstrerror());
62e76326 828 error_html(buf);
cc192b50 829 S.FreeAddrInfo(AI);
62e76326 830 return 1;
d23f4e1b 831 }
62e76326 832
cc192b50 833 S.FreeAddrInfo(AI);
834
7395afb8 835 l = snprintf(buf, sizeof(buf),
62e76326 836 "GET cache_object://%s/%s HTTP/1.0\r\n"
837 "Accept: */*\r\n"
838 "%s" /* Authentication info or nothing */
839 "\r\n",
840 req->hostname,
841 req->action,
842 make_auth_header(req));
d23f4e1b 843 write(s, buf, l);
7395afb8 844 debug(1) fprintf(stderr, "wrote request: '%s'\n", buf);
d23f4e1b 845 return read_reply(s, req);
090089c4 846}
847
684c2720 848int
8203a132 849main(int argc, char *argv[])
090089c4 850{
d23f4e1b 851 char *s;
852 cachemgr_request *req;
62e76326 853
d1a43e28 854 now = time(NULL);
099a1791 855#ifdef _SQUID_MSWIN_
62e76326 856
099a1791 857 Win32SockInit();
a760862a 858 atexit(Win32SockCleanup);
ad1fa9c9 859 _setmode( _fileno( stdin ), _O_BINARY );
860 _setmode( _fileno( stdout ), _O_BINARY );
861 _fmode = _O_BINARY;
862
099a1791 863 if ((s = strrchr(argv[0], '\\')))
864#else
62e76326 865
090089c4 866 if ((s = strrchr(argv[0], '/')))
099a1791 867#endif
62e76326 868
869 progname = xstrdup(s + 1);
090089c4 870 else
62e76326 871 progname = xstrdup(argv[0]);
872
d23f4e1b 873 if ((s = getenv("SCRIPT_NAME")) != NULL)
62e76326 874 script_name = xstrdup(s);
875
d23f4e1b 876 req = read_request();
62e76326 877
d23f4e1b 878 return process_request(req);
090089c4 879}
880
d23f4e1b 881static char *
882read_post_request(void)
090089c4 883{
d23f4e1b 884 char *s;
885 char *buf;
886 int len;
62e76326 887
d23f4e1b 888 if ((s = getenv("REQUEST_METHOD")) == NULL)
62e76326 889 return NULL;
890
d23f4e1b 891 if (0 != strcasecmp(s, "POST"))
62e76326 892 return NULL;
893
d23f4e1b 894 if ((s = getenv("CONTENT_LENGTH")) == NULL)
62e76326 895 return NULL;
896
d23f4e1b 897 if ((len = atoi(s)) <= 0)
62e76326 898 return NULL;
899
e6ccf245 900 buf = (char *)xmalloc(len + 1);
62e76326 901
d23f4e1b 902 fread(buf, len, 1, stdin);
62e76326 903
d23f4e1b 904 buf[len] = '\0';
62e76326 905
d23f4e1b 906 return buf;
907}
090089c4 908
d23f4e1b 909static char *
910read_get_request(void)
911{
912 char *s;
62e76326 913
d23f4e1b 914 if ((s = getenv("QUERY_STRING")) == NULL)
62e76326 915 return NULL;
916
d23f4e1b 917 return xstrdup(s);
918}
090089c4 919
099a1791 920static cachemgr_request *
a760862a 921read_request(void)
099a1791 922{
d23f4e1b 923 char *buf;
62e76326 924
d23f4e1b 925 cachemgr_request *req;
926 char *s;
927 char *t;
928 char *q;
62e76326 929
d23f4e1b 930 if ((buf = read_post_request()) != NULL)
62e76326 931 (void) 0;
d23f4e1b 932 else if ((buf = read_get_request()) != NULL)
62e76326 933 (void) 0;
233794cd 934 else
62e76326 935 return NULL;
936
099a1791 937#ifdef _SQUID_MSWIN_
62e76326 938
099a1791 939 if (strlen(buf) == 0 || strlen(buf) == 4000)
940#else
62e76326 941
d23f4e1b 942 if (strlen(buf) == 0)
099a1791 943#endif
368520fc 944 {
945 free(buf);
62e76326 946 return NULL;
368520fc 947 }
62e76326 948
e6ccf245 949 req = (cachemgr_request *)xcalloc(1, sizeof(cachemgr_request));
62e76326 950
d23f4e1b 951 for (s = strtok(buf, "&"); s != NULL; s = strtok(NULL, "&")) {
62e76326 952 t = xstrdup(s);
953
954 if ((q = strchr(t, '=')) == NULL)
955 continue;
956
957 *q++ = '\0';
958
42ad37af 959 rfc1738_unescape(t);
960
961 rfc1738_unescape(q);
962
963 if (0 == strcasecmp(t, "server") && strlen(q))
964 req->server = xstrdup(q);
965 else if (0 == strcasecmp(t, "host") && strlen(q))
62e76326 966 req->hostname = xstrdup(q);
967 else if (0 == strcasecmp(t, "port") && strlen(q))
968 req->port = atoi(q);
969 else if (0 == strcasecmp(t, "user_name") && strlen(q))
970 req->user_name = xstrdup(q);
971 else if (0 == strcasecmp(t, "passwd") && strlen(q))
972 req->passwd = xstrdup(q);
973 else if (0 == strcasecmp(t, "auth") && strlen(q))
974 req->pub_auth = xstrdup(q), decode_pub_auth(req);
975 else if (0 == strcasecmp(t, "operation"))
976 req->action = xstrdup(q);
d23f4e1b 977 }
62e76326 978
42ad37af 979 if (req->server && !req->hostname) {
980 char *p;
981 req->hostname = strtok(req->server, ":");
982
983 if ((p = strtok(NULL, ":")))
984 req->port = atoi(p);
985 }
986
7395afb8 987 make_pub_auth(req);
988 debug(1) fprintf(stderr, "cmgr: got req: host: '%s' port: %d uname: '%s' passwd: '%s' auth: '%s' oper: '%s'\n",
62e76326 989 safe_str(req->hostname), req->port, safe_str(req->user_name), safe_str(req->passwd), safe_str(req->pub_auth), safe_str(req->action));
d23f4e1b 990 return req;
090089c4 991}
7395afb8 992
993
994/* Routines to support authentication */
995
996/*
997 * Encodes auth info into a "public" form.
998 * Currently no powerful encryption is used.
999 */
1000static void
a760862a 1001make_pub_auth(cachemgr_request * req)
1002{
7395afb8 1003 static char buf[1024];
1004 safe_free(req->pub_auth);
1005 debug(3) fprintf(stderr, "cmgr: encoding for pub...\n");
62e76326 1006
7021844c 1007 if (!req->passwd || !strlen(req->passwd))
62e76326 1008 return;
1009
7395afb8 1010 /* host | time | user | passwd */
1011 snprintf(buf, sizeof(buf), "%s|%d|%s|%s",
62e76326 1012 req->hostname,
1013 (int) now,
1014 req->user_name ? req->user_name : "",
1015 req->passwd);
1016
7395afb8 1017 debug(3) fprintf(stderr, "cmgr: pre-encoded for pub: %s\n", buf);
62e76326 1018
7395afb8 1019 debug(3) fprintf(stderr, "cmgr: encoded: '%s'\n", base64_encode(buf));
62e76326 1020
7395afb8 1021 req->pub_auth = xstrdup(base64_encode(buf));
1022}
1023
1024static void
a760862a 1025decode_pub_auth(cachemgr_request * req)
1026{
7395afb8 1027 char *buf;
1028 const char *host_name;
1029 const char *time_str;
1030 const char *user_name;
1031 const char *passwd;
1032
1033 debug(2) fprintf(stderr, "cmgr: decoding pub: '%s'\n", safe_str(req->pub_auth));
1034 safe_free(req->passwd);
62e76326 1035
7395afb8 1036 if (!req->pub_auth || strlen(req->pub_auth) < 4 + strlen(safe_str(req->hostname)))
62e76326 1037 return;
1038
7395afb8 1039 buf = xstrdup(base64_decode(req->pub_auth));
62e76326 1040
7395afb8 1041 debug(3) fprintf(stderr, "cmgr: length ok\n");
62e76326 1042
7395afb8 1043 /* parse ( a lot of memory leaks, but that is cachemgr style :) */
1044 if ((host_name = strtok(buf, "|")) == NULL)
62e76326 1045 return;
1046
7395afb8 1047 debug(3) fprintf(stderr, "cmgr: decoded host: '%s'\n", host_name);
62e76326 1048
7395afb8 1049 if ((time_str = strtok(NULL, "|")) == NULL)
62e76326 1050 return;
1051
2ac76861 1052 debug(3) fprintf(stderr, "cmgr: decoded time: '%s' (now: %d)\n", time_str, (int) now);
62e76326 1053
7395afb8 1054 if ((user_name = strtok(NULL, "|")) == NULL)
62e76326 1055 return;
1056
7395afb8 1057 debug(3) fprintf(stderr, "cmgr: decoded uname: '%s'\n", user_name);
62e76326 1058
7395afb8 1059 if ((passwd = strtok(NULL, "|")) == NULL)
62e76326 1060 return;
1061
7395afb8 1062 debug(2) fprintf(stderr, "cmgr: decoded passwd: '%s'\n", passwd);
62e76326 1063
7395afb8 1064 /* verify freshness and validity */
1065 if (atoi(time_str) + passwd_ttl < now)
62e76326 1066 return;
1067
7395afb8 1068 if (strcasecmp(host_name, req->hostname))
62e76326 1069 return;
1070
7395afb8 1071 debug(1) fprintf(stderr, "cmgr: verified auth. info.\n");
62e76326 1072
7395afb8 1073 /* ok, accept */
1074 xfree(req->user_name);
62e76326 1075
7395afb8 1076 req->user_name = xstrdup(user_name);
62e76326 1077
7395afb8 1078 req->passwd = xstrdup(passwd);
62e76326 1079
7395afb8 1080 xfree(buf);
1081}
1082
1083static void
a760862a 1084reset_auth(cachemgr_request * req)
1085{
7395afb8 1086 safe_free(req->passwd);
1087 safe_free(req->pub_auth);
1088}
1089
1090static const char *
a760862a 1091make_auth_header(const cachemgr_request * req)
1092{
7395afb8 1093 static char buf[1024];
e6ccf245 1094 size_t stringLength = 0;
7395afb8 1095 const char *str64;
62e76326 1096
2ac76861 1097 if (!req->passwd)
62e76326 1098 return "";
7395afb8 1099
2ac76861 1100 snprintf(buf, sizeof(buf), "%s:%s",
62e76326 1101 req->user_name ? req->user_name : "",
1102 req->passwd);
7395afb8 1103
1104 str64 = base64_encode(buf);
62e76326 1105
e6ccf245 1106 stringLength += snprintf(buf, sizeof(buf), "Authorization: Basic %s\r\n", str64);
62e76326 1107
e6ccf245 1108 assert(stringLength < sizeof(buf));
62e76326 1109
e6ccf245 1110 stringLength += snprintf(&buf[stringLength], sizeof(buf) - stringLength,
62e76326 1111 "Proxy-Authorization: Basic %s\r\n", str64);
1112
7395afb8 1113 return buf;
1114}
42ad37af 1115
1116static int
a760862a 1117check_target_acl(const char *hostname, int port)
1118{
42ad37af 1119 char config_line[BUFSIZ];
1120 FILE *fp = NULL;
1121 int ret = 0;
1122 fp = fopen("cachemgr.conf", "r");
1123
1124 if (fp == NULL)
1125 fp = fopen(DEFAULT_CACHEMGR_CONFIG, "r");
1126
1127 if (fp == NULL) {
1128#ifdef CACHEMGR_HOSTNAME_DEFINED
1129
1130 if (strcmp(hostname, CACHEMGR_HOSTNAME) == 0 && port == CACHE_HTTP_PORT)
1131 return 1;
1132
1133#else
1134
1135 if (strcmp(hostname, "localhost") == 0)
1136 return 1;
1137
1138 if (strcmp(hostname, getfullhostname()) == 0)
1139 return 1;
1140
1141#endif
1142
1143 return 0;
1144 }
1145
1146 while (fgets(config_line, BUFSIZ, fp)) {
1147 char *token = NULL;
1148 strtok(config_line, " \r\n\t");
1149
1150 if (config_line[0] == '#')
1151 continue;
1152
1153 if (config_line[0] == '\0')
1154 continue;
1155
1156 if ((token = strtok(config_line, ":")) == NULL)
1157 continue;
1158
1159#if HAVE_FNMATCH_H
1160
1161 if (fnmatch(token, hostname, 0) != 0)
1162 continue;
1163
1164#else
1165
1166 if (strcmp(token, hostname) != 0)
1167 continue;
1168
1169#endif
1170
1171 if ((token = strtok(NULL, ":")) != NULL) {
1172 int i;
1173
b4ee1908 1174 if (strcmp(token, "*") == 0)
1175
1176 ; /* Wildcard port specification */
1177 else if (strcasecmp(token, "any") == 0)
1178
1179 ; /* Wildcard port specification */
1180 else if (sscanf(token, "%d", &i) != 1)
42ad37af 1181 continue;
1182
b4ee1908 1183 else if (i != port)
42ad37af 1184 continue;
1185 } else if (port != CACHE_HTTP_PORT)
1186 continue;
1187
1188 ret = 1;
1189
1190 break;
1191 }
1192
1193 fclose(fp);
1194 return ret;
1195}