]> git.ipfire.org Git - thirdparty/squid.git/blame - src/cachemgr.cc
Document the 'carp' cache_peer option
[thirdparty/squid.git] / src / cachemgr.cc
CommitLineData
2ac76861 1
30a4f2a8 2/*
099a1791 3 * $Id: cachemgr.cc,v 1.103 2003/02/04 21:57:15 robertc Exp $
30a4f2a8 4 *
f43e2ec2 5 * DEBUG: section 0 CGI Cache Manager
d23f4e1b 6 * AUTHOR: Duane Wessels
30a4f2a8 7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
30a4f2a8 10 *
2b6662ba 11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
30a4f2a8 19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
e25c139f 24 *
30a4f2a8 25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
e25c139f 29 *
30a4f2a8 30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
d23f4e1b 33 *
30a4f2a8 34 */
234967c9 35
36#include "config.h"
234967c9 37
30a4f2a8 38#if HAVE_UNISTD_H
234967c9 39#include <unistd.h>
30a4f2a8 40#endif
41#if HAVE_STDLIB_H
234967c9 42#include <stdlib.h>
30a4f2a8 43#endif
44#if HAVE_STDIO_H
234967c9 45#include <stdio.h>
30a4f2a8 46#endif
47#if HAVE_SYS_TYPES_H
234967c9 48#include <sys/types.h>
30a4f2a8 49#endif
50#if HAVE_CTYPE_H
234967c9 51#include <ctype.h>
30a4f2a8 52#endif
53#if HAVE_ERRNO_H
234967c9 54#include <errno.h>
30a4f2a8 55#endif
56#if HAVE_FCNTL_H
234967c9 57#include <fcntl.h>
30a4f2a8 58#endif
59#if HAVE_GRP_H
234967c9 60#include <grp.h>
30a4f2a8 61#endif
88738790 62#if HAVE_GNUMALLOC_H
63#include <gnumalloc.h>
64#elif HAVE_MALLOC_H && !defined(_SQUID_FREEBSD_) && !defined(_SQUID_NEXT_)
234967c9 65#include <malloc.h>
983061ed 66#endif
30a4f2a8 67#if HAVE_MEMORY_H
234967c9 68#include <memory.h>
30a4f2a8 69#endif
70#if HAVE_NETDB_H && !defined(_SQUID_NETDB_H_) /* protect NEXTSTEP */
71#define _SQUID_NETDB_H_
234967c9 72#include <netdb.h>
30a4f2a8 73#endif
74#if HAVE_PWD_H
234967c9 75#include <pwd.h>
30a4f2a8 76#endif
77#if HAVE_SIGNAL_H
234967c9 78#include <signal.h>
30a4f2a8 79#endif
80#if HAVE_TIME_H
234967c9 81#include <time.h>
30a4f2a8 82#endif
83#if HAVE_SYS_PARAM_H
234967c9 84#include <sys/param.h>
30a4f2a8 85#endif
86#if HAVE_SYS_TIME_H
234967c9 87#include <sys/time.h>
30a4f2a8 88#endif
89#if HAVE_SYS_RESOURCE_H
234967c9 90#include <sys/resource.h> /* needs sys/time.h above it */
30a4f2a8 91#endif
92#if HAVE_SYS_SOCKET_H
234967c9 93#include <sys/socket.h>
30a4f2a8 94#endif
95#if HAVE_NETINET_IN_H
234967c9 96#include <netinet/in.h>
30a4f2a8 97#endif
98#if HAVE_ARPA_INET_H
234967c9 99#include <arpa/inet.h>
30a4f2a8 100#endif
101#if HAVE_SYS_STAT_H
234967c9 102#include <sys/stat.h>
30a4f2a8 103#endif
104#if HAVE_SYS_UN_H
234967c9 105#include <sys/un.h>
30a4f2a8 106#endif
107#if HAVE_SYS_WAIT_H
234967c9 108#include <sys/wait.h>
30a4f2a8 109#endif
110#if HAVE_LIBC_H
111#include <libc.h>
112#endif
113#if HAVE_STRING_H
234967c9 114#include <string.h>
115#endif
30a4f2a8 116#if HAVE_STRINGS_H
234967c9 117#include <strings.h>
118#endif
234967c9 119#if HAVE_BSTRING_H
120#include <bstring.h>
121#endif
30a4f2a8 122#if HAVE_CRYPT_H
234967c9 123#include <crypt.h>
124#endif
234967c9 125#if HAVE_SYS_SELECT_H
126#include <sys/select.h>
127#endif
090089c4 128
17d2e99d 129#include <assert.h>
130
473471f2 131#include "util.h"
042461c3 132#include "snprintf.h"
090089c4 133
d23f4e1b 134typedef struct {
135 char *hostname;
136 int port;
137 char *action;
7395afb8 138 char *user_name;
d23f4e1b 139 char *passwd;
7395afb8 140 char *pub_auth;
d23f4e1b 141} cachemgr_request;
090089c4 142
d23f4e1b 143/*
7395afb8 144 * Debugging macros (info goes to error_log on your web server)
145 * Note: do not run cache manager with non zero debugging level
146 * if you do not debug, it may write a lot of [sensitive]
147 * information to your error log.
d23f4e1b 148 */
7395afb8 149
150/* debugging level 0 (disabled) - 3 (max) */
151#define DEBUG_LEVEL 0
b9bc2838 152#define debug(level) if ((level) <= DEBUG_LEVEL && DEBUG_LEVEL > 0)
7395afb8 153
154/*
155 * Static variables and constants
156 */
2ac76861 157static const time_t passwd_ttl = 60 * 60 * 3; /* in sec */
0ee4272b 158static const char *script_name = "/cgi-bin/cachemgr.cgi";
0ee4272b 159static const char *progname = NULL;
d1a43e28 160static time_t now;
429fdbec 161static struct in_addr no_addr;
090089c4 162
d23f4e1b 163/*
164 * Function prototypes
165 */
4055bb7e 166#define safe_free(str) { if (str) { xfree(str); (str) = NULL; } }
7395afb8 167static const char *safe_str(const char *str);
a2c963ae 168static const char *xstrtok(char **str, char del);
f5b8bbc4 169static void print_trailer(void);
a2c963ae 170static void auth_html(const char *host, int port, const char *user_name);
d23f4e1b 171static void error_html(const char *msg);
7395afb8 172static char *menu_url(cachemgr_request * req, const char *action);
173static int parse_status_line(const char *sline, const char **statusStr);
099a1791 174#ifdef _SQUID_MSWIN_
175static cachemgr_request *read_request(char *);
176#else
d23f4e1b 177static cachemgr_request *read_request(void);
099a1791 178#endif
d23f4e1b 179static char *read_get_request(void);
180static char *read_post_request(void);
181
2ac76861 182static void make_pub_auth(cachemgr_request * req);
183static void decode_pub_auth(cachemgr_request * req);
184static void reset_auth(cachemgr_request * req);
185static const char *make_auth_header(const cachemgr_request * req);
7395afb8 186
099a1791 187#ifdef _SQUID_MSWIN_
188static int s_iInitCount = 0;
189int Win32SockInit(void)
190{
191 int iVersionRequested;
192 WSADATA wsaData;
193 int err;
194
195 if (s_iInitCount > 0) {
196 s_iInitCount++;
197 return (0);
198 }
199 else if (s_iInitCount < 0)
200 return (s_iInitCount);
201
202 /* s_iInitCount == 0. Do the initailization */
203 iVersionRequested = MAKEWORD(2, 0);
204 err = WSAStartup((WORD) iVersionRequested, &wsaData);
205 if (err) {
206 s_iInitCount = -1;
207 return (s_iInitCount);
208 }
209 if (LOBYTE(wsaData.wVersion) != 2 ||
210 HIBYTE(wsaData.wVersion) != 0) {
211 s_iInitCount = -2;
212 WSACleanup();
213 return (s_iInitCount);
214 }
215 s_iInitCount++;
216 return (s_iInitCount);
217}
218
219void Win32SockCleanup(void)
220{
221 if (--s_iInitCount == 0)
222 WSACleanup();
223 return;
224}
225#endif /* ifdef _SQUID_MSWIN_ */
7395afb8 226
2ac76861 227static const char *
228safe_str(const char *str)
7395afb8 229{
230 return str ? str : "";
231}
232
7021844c 233/* relaxed number format */
234static int
235is_number(const char *str)
236{
237 return strspn(str, "\t -+01234567890./\n") == strlen(str);
238}
239
a2c963ae 240static const char *
2ac76861 241xstrtok(char **str, char del)
7395afb8 242{
243 if (*str) {
244 char *p = strchr(*str, del);
245 char *tok = *str;
246 int len;
247 if (p) {
2ac76861 248 *str = p + 1;
7395afb8 249 *p = '\0';
250 } else
251 *str = NULL;
252 /* trim */
253 len = strlen(tok);
b6a2f15e 254 while (len && xisspace(tok[len - 1]))
2ac76861 255 tok[--len] = '\0';
b6a2f15e 256 while (xisspace(*tok))
2ac76861 257 tok++;
258 return tok;
7395afb8 259 } else
260 return "";
261}
090089c4 262
24382924 263static void
8203a132 264print_trailer(void)
090089c4 265{
df339671 266 printf("<HR noshade size=\"1px\">\n");
090089c4 267 printf("<ADDRESS>\n");
268 printf("Generated %s, by %s/%s@%s\n",
a2794549 269 mkrfc1123(now), progname, VERSION, getfullhostname());
a8f7d3ee 270 printf("</ADDRESS></BODY></HTML>\n");
090089c4 271}
272
24382924 273static void
a2c963ae 274auth_html(const char *host, int port, const char *user_name)
090089c4 275{
2ac76861 276 if (!user_name)
277 user_name = "";
278 if (!host || !strlen(host))
279 host = "localhost";
df339671 280 printf("Content-Type: text/html\r\n\r\n");
281 printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
282 printf("<HTML><HEAD><TITLE>Cache Manager Interface</TITLE>\n");
d8c0128e 283 printf("<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE></HEAD>\n");
a8f7d3ee 284 printf("<BODY><H1>Cache Manager Interface</H1>\n");
f7d9a2ad 285 printf("<P>This is a WWW interface to the instrumentation interface\n");
286 printf("for the Squid object cache.</P>\n");
df339671 287 printf("<HR noshade size=\"1px\">\n");
f23f96e6 288 printf("<FORM METHOD=\"POST\" ACTION=\"%s\">\n", script_name);
d980562f 289 printf("<TABLE BORDER=\"0\" CELLPADDING=\"10\" CELLSPACING=\"1\">\n");
7395afb8 290 printf("<TR><TH ALIGN=\"left\">Cache Host:</TH><TD><INPUT NAME=\"host\" ");
df339671 291 printf("size=\"30\" VALUE=\"%s\"></TD></TR>\n", host);
7395afb8 292 printf("<TR><TH ALIGN=\"left\">Cache Port:</TH><TD><INPUT NAME=\"port\" ");
df339671 293 printf("size=\"30\" VALUE=\"%d\"></TD></TR>\n", port);
7395afb8 294 printf("<TR><TH ALIGN=\"left\">Manager name:</TH><TD><INPUT NAME=\"user_name\" ");
df339671 295 printf("size=\"30\" VALUE=\"%s\"></TD></TR>\n", user_name);
7395afb8 296 printf("<TR><TH ALIGN=\"left\">Password:</TH><TD><INPUT TYPE=\"password\" NAME=\"passwd\" ");
df339671 297 printf("size=\"30\" VALUE=\"\"></TD></TR>\n");
7395afb8 298 printf("</TABLE><BR CLEAR=\"all\">\n");
d23f4e1b 299 printf("<INPUT TYPE=\"submit\" VALUE=\"Continue...\">\n");
7395afb8 300 printf("</FORM>\n");
090089c4 301 print_trailer();
302}
303
d23f4e1b 304static void
305error_html(const char *msg)
090089c4 306{
df339671 307 printf("Content-Type: text/html\r\n\r\n");
308 printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
309 printf("<HTML><HEAD><TITLE>Cache Manager Error</TITLE>\n");
d8c0128e 310 printf("<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE></HEAD>\n");
d23f4e1b 311 printf("<BODY><H1>Cache Manager Error</H1>\n");
312 printf("<P>\n%s</P>\n", msg);
313 print_trailer();
090089c4 314}
315
7395afb8 316/* returns http status extracted from status line or -1 on parsing failure */
317static int
318parse_status_line(const char *sline, const char **statusStr)
319{
320 const char *sp = strchr(sline, ' ');
321 if (statusStr)
322 *statusStr = NULL;
323 if (strncasecmp(sline, "HTTP/", 5) || !sp)
324 return -1;
b6a2f15e 325 while (xisspace(*++sp));
326 if (!xisdigit(*sp))
7395afb8 327 return -1;
328 if (statusStr)
329 *statusStr = sp;
330 return atoi(sp);
331}
332
d23f4e1b 333static char *
7395afb8 334menu_url(cachemgr_request * req, const char *action)
090089c4 335{
d23f4e1b 336 static char url[1024];
137ee196 337 snprintf(url, sizeof(url), "%s?host=%s&port=%d&user_name=%s&operation=%s&auth=%s",
d23f4e1b 338 script_name,
339 req->hostname,
340 req->port,
7395afb8 341 safe_str(req->user_name),
342 action,
343 safe_str(req->pub_auth));
d23f4e1b 344 return url;
090089c4 345}
346
d23f4e1b 347static const char *
348munge_menu_line(const char *buf, cachemgr_request * req)
090089c4 349{
d23f4e1b 350 char *x;
7395afb8 351 const char *a;
352 const char *d;
353 const char *p;
354 char *a_url;
7021844c 355 char *buf_copy;
399e85ea 356 static char html[2 * 1024];
d23f4e1b 357 if (strlen(buf) < 1)
358 return buf;
359 if (*buf != ' ')
360 return buf;
7021844c 361 buf_copy = x = xstrdup(buf);
7395afb8 362 a = xstrtok(&x, '\t');
363 d = xstrtok(&x, '\t');
364 p = xstrtok(&x, '\t');
365 a_url = xstrdup(menu_url(req, a));
366 /* no reason to give a url for a disabled action */
367 if (!strcmp(p, "disabled"))
7021844c 368 snprintf(html, sizeof(html), "<LI type=\"circle\">%s (disabled)<A HREF=\"%s\">.</A>\n", d, a_url);
7395afb8 369 else
2ac76861 370 /* disable a hidden action (requires a password, but password is not in squid.conf) */
7395afb8 371 if (!strcmp(p, "hidden"))
7021844c 372 snprintf(html, sizeof(html), "<LI type=\"circle\">%s (hidden)<A HREF=\"%s\">.</A>\n", d, a_url);
7395afb8 373 else
2ac76861 374 /* disable link if authentication is required and we have no password */
375 if (!strcmp(p, "protected") && !req->passwd)
7021844c 376 snprintf(html, sizeof(html), "<LI type=\"circle\">%s (requires <a href=\"%s\">authentication</a>)<A HREF=\"%s\">.</A>\n",
7395afb8 377 d, menu_url(req, "authenticate"), a_url);
378 else
2ac76861 379 /* highlight protected but probably available entries */
7395afb8 380 if (!strcmp(p, "protected"))
7021844c 381 snprintf(html, sizeof(html), "<LI type=\"square\"><A HREF=\"%s\"><font color=\"#FF0000\">%s</font></A>\n",
7395afb8 382 a_url, d);
383 /* public entry or unknown type of protection */
384 else
7021844c 385 snprintf(html, sizeof(html), "<LI type=\"disk\"><A HREF=\"%s\">%s</A>\n", a_url, d);
7395afb8 386 xfree(a_url);
7021844c 387 xfree(buf_copy);
388 return html;
389}
390
391static const char *
392munge_other_line(const char *buf, cachemgr_request * req)
393{
399e85ea 394 static const char *ttags[] =
395 {"td", "th"};
7021844c 396 static char html[4096];
728da2ee 397 static int table_line_num = 0;
398 static int next_is_header = 0;
7021844c 399 int is_header = 0;
400 const char *ttag;
7021844c 401 char *buf_copy;
b9bc2838 402 char *x, *p;
7021844c 403 int l = 0;
404 /* does it look like a table? */
405 if (!strchr(buf, '\t') || *buf == '\t') {
406 /* nope, just text */
407 snprintf(html, sizeof(html), "%s%s",
408 table_line_num ? "</table>\n<pre>" : "", buf);
409 table_line_num = 0;
410 return html;
411 }
b9bc2838 412 /* start html table */
413 if (!table_line_num) {
22567bb5 414 l += snprintf(html + l, sizeof(html) - l, "</pre><table cellpadding=\"2\" cellspacing=\"1\">\n");
b9bc2838 415 next_is_header = 0;
416 }
417 /* remove '\n' */
418 is_header = (!table_line_num || next_is_header) && !strchr(buf, ':') && !is_number(buf);
7021844c 419 ttag = ttags[is_header];
420 /* record starts */
399e85ea 421 l += snprintf(html + l, sizeof(html) - l, "<tr>");
7021844c 422 /* substitute '\t' */
423 buf_copy = x = xstrdup(buf);
399e85ea 424 if ((p = strchr(x, '\n')))
425 *p = '\0';
b9bc2838 426 while (x && strlen(x)) {
427 int column_span = 1;
428 const char *cell = xstrtok(&x, '\t');
399e85ea 429 while (x && *x == '\t') {
430 column_span++;
431 x++;
432 }
df339671 433 l += snprintf(html + l, sizeof(html) - l, "<%s colspan=\"%d\" align=\"%s\">%s</%s>",
b9bc2838 434 ttag, column_span,
7021844c 435 is_header ? "center" : is_number(cell) ? "right" : "left",
436 cell, ttag);
437 }
438 xfree(buf_copy);
439 /* record ends */
399e85ea 440 l += snprintf(html + l, sizeof(html) - l, "</tr>\n");
b9bc2838 441 next_is_header = is_header && strstr(buf, "\t\t");
7021844c 442 table_line_num++;
d23f4e1b 443 return html;
090089c4 444}
445
d23f4e1b 446static int
447read_reply(int s, cachemgr_request * req)
090089c4 448{
2ac76861 449 char buf[4 * 1024];
099a1791 450#ifdef _SQUID_MSWIN_
451 int reply;
452 FILE *fp = tmpfile();
453#else
d23f4e1b 454 FILE *fp = fdopen(s, "r");
099a1791 455#endif
7395afb8 456 /* interpretation states */
2ac76861 457 enum {
458 isStatusLine, isHeaders, isBodyStart, isBody, isForward, isEof, isForwardEof, isSuccess, isError
459 } istate = isStatusLine;
d23f4e1b 460 int parse_menu = 0;
7395afb8 461 const char *action = req->action;
462 const char *statusStr = NULL;
463 int status = -1;
d23f4e1b 464 if (0 == strlen(req->action))
465 parse_menu = 1;
466 else if (0 == strcasecmp(req->action, "menu"))
467 parse_menu = 1;
468 if (fp == NULL) {
469 perror("fdopen");
470 return 1;
090089c4 471 }
099a1791 472#ifdef _SQUID_MSWIN_
473 while ((reply=recv(s,buf,sizeof(buf),0))>0)
474 fwrite(buf,1,reply,fp);
475 rewind(fp);
476#endif
7395afb8 477 if (parse_menu)
478 action = "menu";
479 /* read reply interpreting one line at a time depending on state */
480 while (istate < isEof) {
481 if (!fgets(buf, sizeof(buf), fp))
482 istate = istate == isForward ? isForwardEof : isEof;
483 switch (istate) {
484 case isStatusLine:
485 /* get HTTP status */
486 /* uncomment the following if you want to debug headers */
2ac76861 487 /* fputs("\r\n\r\n", stdout); */
7395afb8 488 status = parse_status_line(buf, &statusStr);
489 istate = status == 200 ? isHeaders : isForward;
490 /* if cache asks for authentication, we have to reset our info */
491 if (status == 401 || status == 407) {
492 reset_auth(req);
2ac76861 493 status = 403; /* Forbiden, see comments in case isForward: */
7395afb8 494 }
495 /* this is a way to pass HTTP status to the Web server */
496 if (statusStr)
2ac76861 497 printf("Status: %d %s", status, statusStr); /* statusStr has '\n' */
7395afb8 498 break;
499 case isHeaders:
500 /* forward header field */
2ac76861 501 if (!strcmp(buf, "\r\n")) { /* end of headers */
502 fputs("Content-Type: text/html\r\n", stdout); /* add our type */
7395afb8 503 istate = isBodyStart;
504 }
2ac76861 505 if (strncasecmp(buf, "Content-Type:", 13)) /* filter out their type */
506 fputs(buf, stdout);
7395afb8 507 break;
508 case isBodyStart:
df339671 509 printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
82ad9938 510 printf("<HTML><HEAD><TITLE>CacheMgr@%s: %s</TITLE>\n",
2ac76861 511 req->hostname, action);
d8c0128e 512 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}--></STYLE>\n");
82ad9938 513 printf("</HEAD><BODY>\n");
7395afb8 514 if (parse_menu) {
515 printf("<H2><a href=\"%s\">Cache Manager</a> menu for %s:</H2>",
516 menu_url(req, "authenticate"), req->hostname);
517 printf("<UL>\n");
518 } else {
df339671 519 printf("<P><A HREF=\"%s\">%s</A>\n<HR noshade size=\"1px\">\n",
7395afb8 520 menu_url(req, "menu"), "Cache Manager menu");
521 printf("<PRE>\n");
522 }
523 istate = isBody;
2edfbcb7 524 /* yes, fall through, we do not want to loose the first line */
7395afb8 525 case isBody:
526 /* interpret [and reformat] cache response */
d23f4e1b 527 if (parse_menu)
528 fputs(munge_menu_line(buf, req), stdout);
529 else
7021844c 530 fputs(munge_other_line(buf, req), stdout);
7395afb8 531 break;
532 case isForward:
533 /* forward: no modifications allowed */
534 /*
535 * Note: we currently do not know any way to get browser.reply to
536 * 401 to .cgi because web server filters out all auth info. Thus we
537 * disable authentication headers for now.
538 */
2ac76861 539 if (!strncasecmp(buf, "WWW-Authenticate:", 17) || !strncasecmp(buf, "Proxy-Authenticate:", 19)); /* skip */
7395afb8 540 else
2ac76861 541 fputs(buf, stdout);
7395afb8 542 break;
543 case isEof:
544 /* print trailers */
545 if (parse_menu)
546 printf("</UL>\n");
547 else
5e91f8bc 548 printf("</table></PRE>\n");
7395afb8 549 print_trailer();
550 istate = isSuccess;
551 break;
552 case isForwardEof:
553 /* indicate that we finished processing an "error" sequence */
554 istate = isError;
555 break;
556 default:
557 printf("%s: internal bug: invalid state reached: %d", script_name, istate);
558 istate = isError;
559 }
d23f4e1b 560 }
d23f4e1b 561 close(s);
562 return 0;
d1a43e28 563}
090089c4 564
d23f4e1b 565static int
566process_request(cachemgr_request * req)
090089c4 567{
d23f4e1b 568 const struct hostent *hp;
569 static struct sockaddr_in S;
570 int s;
571 int l;
099a1791 572#ifdef _SQUID_MSWIN_
573 int answer;
574#endif
2ac76861 575 static char buf[2 * 1024];
d23f4e1b 576 if (req == NULL) {
7395afb8 577 auth_html(CACHEMGR_HOSTNAME, CACHE_HTTP_PORT, "");
d23f4e1b 578 return 1;
579 }
580 if (req->hostname == NULL) {
581 req->hostname = xstrdup(CACHEMGR_HOSTNAME);
582 }
583 if (req->port == 0) {
584 req->port = CACHE_HTTP_PORT;
585 }
586 if (req->action == NULL) {
587 req->action = xstrdup("");
588 }
7395afb8 589 if (!strcmp(req->action, "authenticate")) {
590 auth_html(req->hostname, req->port, req->user_name);
591 return 0;
592 }
d23f4e1b 593 if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
594 snprintf(buf, 1024, "socket: %s\n", xstrerror());
595 error_html(buf);
596 return 1;
597 }
598 memset(&S, '\0', sizeof(struct sockaddr_in));
599 S.sin_family = AF_INET;
7fdc7d5d 600 if ((hp = gethostbyname(req->hostname)) != NULL) {
e6ccf245 601 assert(hp->h_length >= 0 && (size_t)hp->h_length <= sizeof(S.sin_addr.s_addr));
d23f4e1b 602 xmemcpy(&S.sin_addr.s_addr, hp->h_addr, hp->h_length);
fa80a8ef 603 } else if (safe_inet_addr(req->hostname, &S.sin_addr))
d23f4e1b 604 (void) 0;
605 else {
606 snprintf(buf, 1024, "Unknown host: %s\n", req->hostname);
607 error_html(buf);
608 return 1;
609 }
610 S.sin_port = htons(req->port);
611 if (connect(s, (struct sockaddr *) &S, sizeof(struct sockaddr_in)) < 0) {
612 snprintf(buf, 1024, "connect: %s\n", xstrerror());
613 error_html(buf);
614 return 1;
615 }
7395afb8 616 l = snprintf(buf, sizeof(buf),
d23f4e1b 617 "GET cache_object://%s/%s HTTP/1.0\r\n"
618 "Accept: */*\r\n"
2ac76861 619 "%s" /* Authentication info or nothing */
d23f4e1b 620 "\r\n",
621 req->hostname,
7395afb8 622 req->action,
623 make_auth_header(req));
d23f4e1b 624 write(s, buf, l);
7395afb8 625 debug(1) fprintf(stderr, "wrote request: '%s'\n", buf);
099a1791 626#ifdef _SQUID_MSWIN_
627 answer=read_reply(s, req);
628 close(s);
629 return answer;
630#else
d23f4e1b 631 return read_reply(s, req);
099a1791 632#endif
090089c4 633}
634
684c2720 635int
8203a132 636main(int argc, char *argv[])
090089c4 637{
d23f4e1b 638 char *s;
639 cachemgr_request *req;
099a1791 640#ifdef _SQUID_MSWIN_
641 int answer;
642#endif
429fdbec 643 safe_inet_addr("255.255.255.255", &no_addr);
d1a43e28 644 now = time(NULL);
099a1791 645#ifdef _SQUID_MSWIN_
646 Win32SockInit();
647 if ((s = strrchr(argv[0], '\\')))
648#else
090089c4 649 if ((s = strrchr(argv[0], '/')))
099a1791 650#endif
3c0117c9 651 progname = xstrdup(s + 1);
090089c4 652 else
3c0117c9 653 progname = xstrdup(argv[0]);
d23f4e1b 654 if ((s = getenv("SCRIPT_NAME")) != NULL)
3c0117c9 655 script_name = xstrdup(s);
099a1791 656#ifdef _SQUID_MSWIN_
657 req = read_request(NULL);
658 answer=process_request(req);
659 Win32SockCleanup();
660 return answer;
661#else
d23f4e1b 662 req = read_request();
663 return process_request(req);
099a1791 664#endif
090089c4 665}
666
d23f4e1b 667static char *
668read_post_request(void)
090089c4 669{
d23f4e1b 670 char *s;
671 char *buf;
672 int len;
673 if ((s = getenv("REQUEST_METHOD")) == NULL)
674 return NULL;
675 if (0 != strcasecmp(s, "POST"))
676 return NULL;
677 if ((s = getenv("CONTENT_LENGTH")) == NULL)
678 return NULL;
679 if ((len = atoi(s)) <= 0)
680 return NULL;
e6ccf245 681 buf = (char *)xmalloc(len + 1);
d23f4e1b 682 fread(buf, len, 1, stdin);
683 buf[len] = '\0';
684 return buf;
685}
090089c4 686
d23f4e1b 687static char *
688read_get_request(void)
689{
690 char *s;
691 if ((s = getenv("QUERY_STRING")) == NULL)
692 return NULL;
693 return xstrdup(s);
694}
090089c4 695
099a1791 696#ifdef _SQUID_MSWIN_
697static cachemgr_request *
698read_request(char* buf)
699{
700#else
d23f4e1b 701static cachemgr_request *
702read_request(void)
703{
704 char *buf;
099a1791 705#endif
d23f4e1b 706 cachemgr_request *req;
707 char *s;
708 char *t;
709 char *q;
710 if ((buf = read_post_request()) != NULL)
711 (void) 0;
712 else if ((buf = read_get_request()) != NULL)
429fdbec 713 (void) 0;
233794cd 714 else
d23f4e1b 715 return NULL;
099a1791 716#ifdef _SQUID_MSWIN_
717 if (strlen(buf) == 0 || strlen(buf) == 4000)
718#else
d23f4e1b 719 if (strlen(buf) == 0)
099a1791 720#endif
d23f4e1b 721 return NULL;
e6ccf245 722 req = (cachemgr_request *)xcalloc(1, sizeof(cachemgr_request));
d23f4e1b 723 for (s = strtok(buf, "&"); s != NULL; s = strtok(NULL, "&")) {
724 t = xstrdup(s);
725 if ((q = strchr(t, '=')) == NULL)
726 continue;
727 *q++ = '\0';
7395afb8 728 if (0 == strcasecmp(t, "host") && strlen(q))
d23f4e1b 729 req->hostname = xstrdup(q);
2ac76861 730 else if (0 == strcasecmp(t, "port") && strlen(q))
d23f4e1b 731 req->port = atoi(q);
2ac76861 732 else if (0 == strcasecmp(t, "user_name") && strlen(q))
7395afb8 733 req->user_name = xstrdup(q);
2ac76861 734 else if (0 == strcasecmp(t, "passwd") && strlen(q))
d23f4e1b 735 req->passwd = xstrdup(q);
2ac76861 736 else if (0 == strcasecmp(t, "auth") && strlen(q))
7395afb8 737 req->pub_auth = xstrdup(q), decode_pub_auth(req);
2ac76861 738 else if (0 == strcasecmp(t, "operation"))
d23f4e1b 739 req->action = xstrdup(q);
740 }
7395afb8 741 make_pub_auth(req);
742 debug(1) fprintf(stderr, "cmgr: got req: host: '%s' port: %d uname: '%s' passwd: '%s' auth: '%s' oper: '%s'\n",
743 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 744 return req;
090089c4 745}
7395afb8 746
747
748/* Routines to support authentication */
749
750/*
751 * Encodes auth info into a "public" form.
752 * Currently no powerful encryption is used.
753 */
754static void
2ac76861 755make_pub_auth(cachemgr_request * req)
7395afb8 756{
757 static char buf[1024];
758 safe_free(req->pub_auth);
759 debug(3) fprintf(stderr, "cmgr: encoding for pub...\n");
7021844c 760 if (!req->passwd || !strlen(req->passwd))
7395afb8 761 return;
762 /* host | time | user | passwd */
763 snprintf(buf, sizeof(buf), "%s|%d|%s|%s",
764 req->hostname,
1afe05c5 765 (int) now,
7395afb8 766 req->user_name ? req->user_name : "",
767 req->passwd);
768 debug(3) fprintf(stderr, "cmgr: pre-encoded for pub: %s\n", buf);
769 debug(3) fprintf(stderr, "cmgr: encoded: '%s'\n", base64_encode(buf));
770 req->pub_auth = xstrdup(base64_encode(buf));
771}
772
773static void
2ac76861 774decode_pub_auth(cachemgr_request * req)
7395afb8 775{
776 char *buf;
777 const char *host_name;
778 const char *time_str;
779 const char *user_name;
780 const char *passwd;
781
782 debug(2) fprintf(stderr, "cmgr: decoding pub: '%s'\n", safe_str(req->pub_auth));
783 safe_free(req->passwd);
784 if (!req->pub_auth || strlen(req->pub_auth) < 4 + strlen(safe_str(req->hostname)))
785 return;
786 buf = xstrdup(base64_decode(req->pub_auth));
787 debug(3) fprintf(stderr, "cmgr: length ok\n");
788 /* parse ( a lot of memory leaks, but that is cachemgr style :) */
789 if ((host_name = strtok(buf, "|")) == NULL)
790 return;
791 debug(3) fprintf(stderr, "cmgr: decoded host: '%s'\n", host_name);
792 if ((time_str = strtok(NULL, "|")) == NULL)
793 return;
2ac76861 794 debug(3) fprintf(stderr, "cmgr: decoded time: '%s' (now: %d)\n", time_str, (int) now);
7395afb8 795 if ((user_name = strtok(NULL, "|")) == NULL)
796 return;
797 debug(3) fprintf(stderr, "cmgr: decoded uname: '%s'\n", user_name);
798 if ((passwd = strtok(NULL, "|")) == NULL)
799 return;
800 debug(2) fprintf(stderr, "cmgr: decoded passwd: '%s'\n", passwd);
801 /* verify freshness and validity */
802 if (atoi(time_str) + passwd_ttl < now)
803 return;
804 if (strcasecmp(host_name, req->hostname))
805 return;
806 debug(1) fprintf(stderr, "cmgr: verified auth. info.\n");
807 /* ok, accept */
808 xfree(req->user_name);
809 req->user_name = xstrdup(user_name);
810 req->passwd = xstrdup(passwd);
811 xfree(buf);
812}
813
814static void
2ac76861 815reset_auth(cachemgr_request * req)
7395afb8 816{
817 safe_free(req->passwd);
818 safe_free(req->pub_auth);
819}
820
821static const char *
2ac76861 822make_auth_header(const cachemgr_request * req)
7395afb8 823{
824 static char buf[1024];
e6ccf245 825 size_t stringLength = 0;
7395afb8 826 const char *str64;
2ac76861 827 if (!req->passwd)
7395afb8 828 return "";
829
2ac76861 830 snprintf(buf, sizeof(buf), "%s:%s",
7395afb8 831 req->user_name ? req->user_name : "",
2ac76861 832 req->passwd);
7395afb8 833
834 str64 = base64_encode(buf);
e6ccf245 835 stringLength += snprintf(buf, sizeof(buf), "Authorization: Basic %s\r\n", str64);
836 assert(stringLength < sizeof(buf));
837 stringLength += snprintf(&buf[stringLength], sizeof(buf) - stringLength,
b6a2f15e 838 "Proxy-Authorization: Basic %s\r\n", str64);
7395afb8 839 return buf;
840}