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