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