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