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