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