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