]>
Commit | Line | Data |
---|---|---|
2ac76861 | 1 | |
30a4f2a8 | 2 | /* |
b6a2f15e | 3 | * $Id: cachemgr.cc,v 1.85 1999/04/15 06:15:46 wessels Exp $ |
30a4f2a8 | 4 | * |
f43e2ec2 | 5 | * DEBUG: section 0 CGI Cache Manager |
d23f4e1b | 6 | * AUTHOR: Duane Wessels |
30a4f2a8 | 7 | * |
42c04c16 | 8 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ |
e25c139f | 9 | * ---------------------------------------------------------- |
30a4f2a8 | 10 | * |
11 | * Squid is the result of efforts by numerous individuals from the | |
12 | * Internet community. Development is led by Duane Wessels of the | |
e25c139f | 13 | * National Laboratory for Applied Network Research and funded by the |
14 | * National Science Foundation. Squid is Copyrighted (C) 1998 by | |
15 | * Duane Wessels and the University of California San Diego. Please | |
16 | * see the COPYRIGHT file for full details. Squid incorporates | |
17 | * software developed and/or copyrighted by other sources. Please see | |
18 | * 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 | |
473471f2 | 129 | #include "util.h" |
042461c3 | 130 | #include "snprintf.h" |
090089c4 | 131 | |
d23f4e1b | 132 | typedef struct { |
133 | char *hostname; | |
134 | int port; | |
135 | char *action; | |
7395afb8 | 136 | char *user_name; |
d23f4e1b | 137 | char *passwd; |
7395afb8 | 138 | char *pub_auth; |
d23f4e1b | 139 | } cachemgr_request; |
090089c4 | 140 | |
d23f4e1b | 141 | /* |
7395afb8 | 142 | * Debugging macros (info goes to error_log on your web server) |
143 | * Note: do not run cache manager with non zero debugging level | |
144 | * if you do not debug, it may write a lot of [sensitive] | |
145 | * information to your error log. | |
d23f4e1b | 146 | */ |
7395afb8 | 147 | |
148 | /* debugging level 0 (disabled) - 3 (max) */ | |
149 | #define DEBUG_LEVEL 0 | |
b9bc2838 | 150 | #define debug(level) if ((level) <= DEBUG_LEVEL && DEBUG_LEVEL > 0) |
7395afb8 | 151 | |
152 | /* | |
153 | * Static variables and constants | |
154 | */ | |
2ac76861 | 155 | static const time_t passwd_ttl = 60 * 60 * 3; /* in sec */ |
0ee4272b | 156 | static const char *script_name = "/cgi-bin/cachemgr.cgi"; |
0ee4272b | 157 | static const char *progname = NULL; |
d1a43e28 | 158 | static time_t now; |
429fdbec | 159 | static struct in_addr no_addr; |
090089c4 | 160 | |
d23f4e1b | 161 | /* |
162 | * Function prototypes | |
163 | */ | |
4055bb7e | 164 | #define safe_free(str) { if (str) { xfree(str); (str) = NULL; } } |
7395afb8 | 165 | static const char *safe_str(const char *str); |
166 | static char *xstrtok(char **str, char del); | |
f5b8bbc4 | 167 | static void print_trailer(void); |
7395afb8 | 168 | static void auth_html(char *host, int port, const char *user_name); |
d23f4e1b | 169 | static void error_html(const char *msg); |
7395afb8 | 170 | static char *menu_url(cachemgr_request * req, const char *action); |
171 | static int parse_status_line(const char *sline, const char **statusStr); | |
d23f4e1b | 172 | static cachemgr_request *read_request(void); |
173 | static char *read_get_request(void); | |
174 | static char *read_post_request(void); | |
175 | ||
2ac76861 | 176 | static void make_pub_auth(cachemgr_request * req); |
177 | static void decode_pub_auth(cachemgr_request * req); | |
178 | static void reset_auth(cachemgr_request * req); | |
179 | static const char *make_auth_header(const cachemgr_request * req); | |
7395afb8 | 180 | |
181 | ||
2ac76861 | 182 | static const char * |
183 | safe_str(const char *str) | |
7395afb8 | 184 | { |
185 | return str ? str : ""; | |
186 | } | |
187 | ||
7021844c | 188 | /* relaxed number format */ |
189 | static int | |
190 | is_number(const char *str) | |
191 | { | |
192 | return strspn(str, "\t -+01234567890./\n") == strlen(str); | |
193 | } | |
194 | ||
2ac76861 | 195 | static char * |
196 | xstrtok(char **str, char del) | |
7395afb8 | 197 | { |
198 | if (*str) { | |
199 | char *p = strchr(*str, del); | |
200 | char *tok = *str; | |
201 | int len; | |
202 | if (p) { | |
2ac76861 | 203 | *str = p + 1; |
7395afb8 | 204 | *p = '\0'; |
205 | } else | |
206 | *str = NULL; | |
207 | /* trim */ | |
208 | len = strlen(tok); | |
b6a2f15e | 209 | while (len && xisspace(tok[len - 1])) |
2ac76861 | 210 | tok[--len] = '\0'; |
b6a2f15e | 211 | while (xisspace(*tok)) |
2ac76861 | 212 | tok++; |
213 | return tok; | |
7395afb8 | 214 | } else |
215 | return ""; | |
216 | } | |
090089c4 | 217 | |
24382924 | 218 | static void |
8203a132 | 219 | print_trailer(void) |
090089c4 | 220 | { |
090089c4 | 221 | printf("<HR>\n"); |
222 | printf("<ADDRESS>\n"); | |
223 | printf("Generated %s, by %s/%s@%s\n", | |
72367f90 | 224 | mkrfc1123(now), progname, SQUID_VERSION, getfullhostname()); |
a8f7d3ee | 225 | printf("</ADDRESS></BODY></HTML>\n"); |
090089c4 | 226 | } |
227 | ||
24382924 | 228 | static void |
7395afb8 | 229 | auth_html(char *host, int port, const char *user_name) |
090089c4 | 230 | { |
2ac76861 | 231 | if (!user_name) |
232 | user_name = ""; | |
233 | if (!host || !strlen(host)) | |
234 | host = "localhost"; | |
d23f4e1b | 235 | printf("Content-type: text/html\r\n\r\n"); |
a8f7d3ee | 236 | printf("<HTML><HEAD><TITLE>Cache Manager Interface</TITLE></HEAD>\n"); |
237 | printf("<BODY><H1>Cache Manager Interface</H1>\n"); | |
f7d9a2ad | 238 | printf("<P>This is a WWW interface to the instrumentation interface\n"); |
239 | printf("for the Squid object cache.</P>\n"); | |
090089c4 | 240 | printf("<HR>\n"); |
bce0cf8c | 241 | printf("<FORM METHOD=\"GET\" ACTION=\"%s\">\n", script_name); |
7395afb8 | 242 | printf("<TABLE BORDER=0>\n"); |
243 | printf("<TR><TH ALIGN=\"left\">Cache Host:</TH><TD><INPUT NAME=\"host\" "); | |
12cf1be2 | 244 | printf("SIZE=30 VALUE=\"%s\"></TD></TR>\n", host); |
7395afb8 | 245 | printf("<TR><TH ALIGN=\"left\">Cache Port:</TH><TD><INPUT NAME=\"port\" "); |
246 | printf("SIZE=30 VALUE=\"%d\"></TD></TR>\n", port); | |
247 | printf("<TR><TH ALIGN=\"left\">Manager name:</TH><TD><INPUT NAME=\"user_name\" "); | |
248 | printf("SIZE=30 VALUE=\"%s\"></TD></TR>\n", user_name); | |
249 | printf("<TR><TH ALIGN=\"left\">Password:</TH><TD><INPUT TYPE=\"password\" NAME=\"passwd\" "); | |
250 | printf("SIZE=30 VALUE=\"\"></TD></TR>\n"); | |
251 | printf("</TABLE><BR CLEAR=\"all\">\n"); | |
d23f4e1b | 252 | printf("<INPUT TYPE=\"submit\" VALUE=\"Continue...\">\n"); |
7395afb8 | 253 | printf("</FORM>\n"); |
090089c4 | 254 | print_trailer(); |
255 | } | |
256 | ||
d23f4e1b | 257 | static void |
258 | error_html(const char *msg) | |
090089c4 | 259 | { |
d23f4e1b | 260 | printf("Content-type: text/html\r\n\r\n"); |
261 | printf("<HTML><HEAD><TITLE>Cache Manager Error</TITLE></HEAD>\n"); | |
262 | printf("<BODY><H1>Cache Manager Error</H1>\n"); | |
263 | printf("<P>\n%s</P>\n", msg); | |
264 | print_trailer(); | |
090089c4 | 265 | } |
266 | ||
7395afb8 | 267 | /* returns http status extracted from status line or -1 on parsing failure */ |
268 | static int | |
269 | parse_status_line(const char *sline, const char **statusStr) | |
270 | { | |
271 | const char *sp = strchr(sline, ' '); | |
272 | if (statusStr) | |
273 | *statusStr = NULL; | |
274 | if (strncasecmp(sline, "HTTP/", 5) || !sp) | |
275 | return -1; | |
b6a2f15e | 276 | while (xisspace(*++sp)); |
277 | if (!xisdigit(*sp)) | |
7395afb8 | 278 | return -1; |
279 | if (statusStr) | |
280 | *statusStr = sp; | |
281 | return atoi(sp); | |
282 | } | |
283 | ||
d23f4e1b | 284 | static char * |
7395afb8 | 285 | menu_url(cachemgr_request * req, const char *action) |
090089c4 | 286 | { |
d23f4e1b | 287 | static char url[1024]; |
137ee196 | 288 | snprintf(url, sizeof(url), "%s?host=%s&port=%d&user_name=%s&operation=%s&auth=%s", |
d23f4e1b | 289 | script_name, |
290 | req->hostname, | |
291 | req->port, | |
7395afb8 | 292 | safe_str(req->user_name), |
293 | action, | |
294 | safe_str(req->pub_auth)); | |
d23f4e1b | 295 | return url; |
090089c4 | 296 | } |
297 | ||
d23f4e1b | 298 | static const char * |
299 | munge_menu_line(const char *buf, cachemgr_request * req) | |
090089c4 | 300 | { |
d23f4e1b | 301 | char *x; |
7395afb8 | 302 | const char *a; |
303 | const char *d; | |
304 | const char *p; | |
305 | char *a_url; | |
7021844c | 306 | char *buf_copy; |
399e85ea | 307 | static char html[2 * 1024]; |
d23f4e1b | 308 | if (strlen(buf) < 1) |
309 | return buf; | |
310 | if (*buf != ' ') | |
311 | return buf; | |
7021844c | 312 | buf_copy = x = xstrdup(buf); |
7395afb8 | 313 | a = xstrtok(&x, '\t'); |
314 | d = xstrtok(&x, '\t'); | |
315 | p = xstrtok(&x, '\t'); | |
316 | a_url = xstrdup(menu_url(req, a)); | |
317 | /* no reason to give a url for a disabled action */ | |
318 | if (!strcmp(p, "disabled")) | |
7021844c | 319 | snprintf(html, sizeof(html), "<LI type=\"circle\">%s (disabled)<A HREF=\"%s\">.</A>\n", d, a_url); |
7395afb8 | 320 | else |
2ac76861 | 321 | /* disable a hidden action (requires a password, but password is not in squid.conf) */ |
7395afb8 | 322 | if (!strcmp(p, "hidden")) |
7021844c | 323 | snprintf(html, sizeof(html), "<LI type=\"circle\">%s (hidden)<A HREF=\"%s\">.</A>\n", d, a_url); |
7395afb8 | 324 | else |
2ac76861 | 325 | /* disable link if authentication is required and we have no password */ |
326 | if (!strcmp(p, "protected") && !req->passwd) | |
7021844c | 327 | snprintf(html, sizeof(html), "<LI type=\"circle\">%s (requires <a href=\"%s\">authentication</a>)<A HREF=\"%s\">.</A>\n", |
7395afb8 | 328 | d, menu_url(req, "authenticate"), a_url); |
329 | else | |
2ac76861 | 330 | /* highlight protected but probably available entries */ |
7395afb8 | 331 | if (!strcmp(p, "protected")) |
7021844c | 332 | snprintf(html, sizeof(html), "<LI type=\"square\"><A HREF=\"%s\"><font color=\"#FF0000\">%s</font></A>\n", |
7395afb8 | 333 | a_url, d); |
334 | /* public entry or unknown type of protection */ | |
335 | else | |
7021844c | 336 | snprintf(html, sizeof(html), "<LI type=\"disk\"><A HREF=\"%s\">%s</A>\n", a_url, d); |
7395afb8 | 337 | xfree(a_url); |
7021844c | 338 | xfree(buf_copy); |
339 | return html; | |
340 | } | |
341 | ||
342 | static const char * | |
343 | munge_other_line(const char *buf, cachemgr_request * req) | |
344 | { | |
399e85ea | 345 | static const char *ttags[] = |
346 | {"td", "th"}; | |
7021844c | 347 | static char html[4096]; |
728da2ee | 348 | static int table_line_num = 0; |
349 | static int next_is_header = 0; | |
7021844c | 350 | int is_header = 0; |
351 | const char *ttag; | |
7021844c | 352 | char *buf_copy; |
b9bc2838 | 353 | char *x, *p; |
7021844c | 354 | int l = 0; |
355 | /* does it look like a table? */ | |
356 | if (!strchr(buf, '\t') || *buf == '\t') { | |
357 | /* nope, just text */ | |
358 | snprintf(html, sizeof(html), "%s%s", | |
359 | table_line_num ? "</table>\n<pre>" : "", buf); | |
360 | table_line_num = 0; | |
361 | return html; | |
362 | } | |
b9bc2838 | 363 | /* start html table */ |
364 | if (!table_line_num) { | |
399e85ea | 365 | l += snprintf(html + l, sizeof(html) - l, "</pre><table border=1 cellpadding=2 cellspacing=1>\n"); |
b9bc2838 | 366 | next_is_header = 0; |
367 | } | |
368 | /* remove '\n' */ | |
369 | is_header = (!table_line_num || next_is_header) && !strchr(buf, ':') && !is_number(buf); | |
7021844c | 370 | ttag = ttags[is_header]; |
371 | /* record starts */ | |
399e85ea | 372 | l += snprintf(html + l, sizeof(html) - l, "<tr>"); |
7021844c | 373 | /* substitute '\t' */ |
374 | buf_copy = x = xstrdup(buf); | |
399e85ea | 375 | if ((p = strchr(x, '\n'))) |
376 | *p = '\0'; | |
b9bc2838 | 377 | while (x && strlen(x)) { |
378 | int column_span = 1; | |
379 | const char *cell = xstrtok(&x, '\t'); | |
399e85ea | 380 | while (x && *x == '\t') { |
381 | column_span++; | |
382 | x++; | |
383 | } | |
384 | l += snprintf(html + l, sizeof(html) - l, "<%s colspan=%d align=\"%s\">%s</%s>", | |
b9bc2838 | 385 | ttag, column_span, |
7021844c | 386 | is_header ? "center" : is_number(cell) ? "right" : "left", |
387 | cell, ttag); | |
388 | } | |
389 | xfree(buf_copy); | |
390 | /* record ends */ | |
399e85ea | 391 | l += snprintf(html + l, sizeof(html) - l, "</tr>\n"); |
b9bc2838 | 392 | next_is_header = is_header && strstr(buf, "\t\t"); |
7021844c | 393 | table_line_num++; |
d23f4e1b | 394 | return html; |
090089c4 | 395 | } |
396 | ||
d23f4e1b | 397 | static int |
398 | read_reply(int s, cachemgr_request * req) | |
090089c4 | 399 | { |
2ac76861 | 400 | char buf[4 * 1024]; |
d23f4e1b | 401 | FILE *fp = fdopen(s, "r"); |
7395afb8 | 402 | /* interpretation states */ |
2ac76861 | 403 | enum { |
404 | isStatusLine, isHeaders, isBodyStart, isBody, isForward, isEof, isForwardEof, isSuccess, isError | |
405 | } istate = isStatusLine; | |
d23f4e1b | 406 | int parse_menu = 0; |
7395afb8 | 407 | const char *action = req->action; |
408 | const char *statusStr = NULL; | |
409 | int status = -1; | |
d23f4e1b | 410 | if (0 == strlen(req->action)) |
411 | parse_menu = 1; | |
412 | else if (0 == strcasecmp(req->action, "menu")) | |
413 | parse_menu = 1; | |
414 | if (fp == NULL) { | |
415 | perror("fdopen"); | |
416 | return 1; | |
090089c4 | 417 | } |
7395afb8 | 418 | if (parse_menu) |
419 | action = "menu"; | |
420 | /* read reply interpreting one line at a time depending on state */ | |
421 | while (istate < isEof) { | |
422 | if (!fgets(buf, sizeof(buf), fp)) | |
423 | istate = istate == isForward ? isForwardEof : isEof; | |
424 | switch (istate) { | |
425 | case isStatusLine: | |
426 | /* get HTTP status */ | |
427 | /* uncomment the following if you want to debug headers */ | |
2ac76861 | 428 | /* fputs("\r\n\r\n", stdout); */ |
7395afb8 | 429 | status = parse_status_line(buf, &statusStr); |
430 | istate = status == 200 ? isHeaders : isForward; | |
431 | /* if cache asks for authentication, we have to reset our info */ | |
432 | if (status == 401 || status == 407) { | |
433 | reset_auth(req); | |
2ac76861 | 434 | status = 403; /* Forbiden, see comments in case isForward: */ |
7395afb8 | 435 | } |
436 | /* this is a way to pass HTTP status to the Web server */ | |
437 | if (statusStr) | |
2ac76861 | 438 | printf("Status: %d %s", status, statusStr); /* statusStr has '\n' */ |
7395afb8 | 439 | break; |
440 | case isHeaders: | |
441 | /* forward header field */ | |
2ac76861 | 442 | if (!strcmp(buf, "\r\n")) { /* end of headers */ |
443 | fputs("Content-Type: text/html\r\n", stdout); /* add our type */ | |
7395afb8 | 444 | istate = isBodyStart; |
445 | } | |
2ac76861 | 446 | if (strncasecmp(buf, "Content-Type:", 13)) /* filter out their type */ |
447 | fputs(buf, stdout); | |
7395afb8 | 448 | break; |
449 | case isBodyStart: | |
450 | printf("<HTML><HEAD><TITLE>CacheMgr@%s: %s</TITLE></HEAD><BODY>\n", | |
2ac76861 | 451 | req->hostname, action); |
7395afb8 | 452 | if (parse_menu) { |
453 | printf("<H2><a href=\"%s\">Cache Manager</a> menu for %s:</H2>", | |
454 | menu_url(req, "authenticate"), req->hostname); | |
455 | printf("<UL>\n"); | |
456 | } else { | |
457 | printf("<P><A HREF=\"%s\">%s</A>\n<HR>\n", | |
458 | menu_url(req, "menu"), "Cache Manager menu"); | |
459 | printf("<PRE>\n"); | |
460 | } | |
461 | istate = isBody; | |
2edfbcb7 | 462 | /* yes, fall through, we do not want to loose the first line */ |
7395afb8 | 463 | case isBody: |
464 | /* interpret [and reformat] cache response */ | |
d23f4e1b | 465 | if (parse_menu) |
466 | fputs(munge_menu_line(buf, req), stdout); | |
467 | else | |
7021844c | 468 | fputs(munge_other_line(buf, req), stdout); |
7395afb8 | 469 | break; |
470 | case isForward: | |
471 | /* forward: no modifications allowed */ | |
472 | /* | |
473 | * Note: we currently do not know any way to get browser.reply to | |
474 | * 401 to .cgi because web server filters out all auth info. Thus we | |
475 | * disable authentication headers for now. | |
476 | */ | |
2ac76861 | 477 | if (!strncasecmp(buf, "WWW-Authenticate:", 17) || !strncasecmp(buf, "Proxy-Authenticate:", 19)); /* skip */ |
7395afb8 | 478 | else |
2ac76861 | 479 | fputs(buf, stdout); |
7395afb8 | 480 | break; |
481 | case isEof: | |
482 | /* print trailers */ | |
483 | if (parse_menu) | |
484 | printf("</UL>\n"); | |
485 | else | |
5e91f8bc | 486 | printf("</table></PRE>\n"); |
7395afb8 | 487 | print_trailer(); |
488 | istate = isSuccess; | |
489 | break; | |
490 | case isForwardEof: | |
491 | /* indicate that we finished processing an "error" sequence */ | |
492 | istate = isError; | |
493 | break; | |
494 | default: | |
495 | printf("%s: internal bug: invalid state reached: %d", script_name, istate); | |
496 | istate = isError; | |
497 | } | |
d23f4e1b | 498 | } |
d23f4e1b | 499 | close(s); |
500 | return 0; | |
d1a43e28 | 501 | } |
090089c4 | 502 | |
d23f4e1b | 503 | static int |
504 | process_request(cachemgr_request * req) | |
090089c4 | 505 | { |
d23f4e1b | 506 | const struct hostent *hp; |
507 | static struct sockaddr_in S; | |
508 | int s; | |
509 | int l; | |
2ac76861 | 510 | static char buf[2 * 1024]; |
d23f4e1b | 511 | if (req == NULL) { |
7395afb8 | 512 | auth_html(CACHEMGR_HOSTNAME, CACHE_HTTP_PORT, ""); |
d23f4e1b | 513 | return 1; |
514 | } | |
515 | if (req->hostname == NULL) { | |
516 | req->hostname = xstrdup(CACHEMGR_HOSTNAME); | |
517 | } | |
518 | if (req->port == 0) { | |
519 | req->port = CACHE_HTTP_PORT; | |
520 | } | |
521 | if (req->action == NULL) { | |
522 | req->action = xstrdup(""); | |
523 | } | |
7395afb8 | 524 | if (!strcmp(req->action, "authenticate")) { |
525 | auth_html(req->hostname, req->port, req->user_name); | |
526 | return 0; | |
527 | } | |
d23f4e1b | 528 | if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) { |
529 | snprintf(buf, 1024, "socket: %s\n", xstrerror()); | |
530 | error_html(buf); | |
531 | return 1; | |
532 | } | |
533 | memset(&S, '\0', sizeof(struct sockaddr_in)); | |
534 | S.sin_family = AF_INET; | |
535 | if ((hp = gethostbyname(req->hostname)) != NULL) | |
536 | xmemcpy(&S.sin_addr.s_addr, hp->h_addr, hp->h_length); | |
537 | else if (safe_inet_addr(req->hostname, &S.sin_addr)) | |
538 | (void) 0; | |
539 | else { | |
540 | snprintf(buf, 1024, "Unknown host: %s\n", req->hostname); | |
541 | error_html(buf); | |
542 | return 1; | |
543 | } | |
544 | S.sin_port = htons(req->port); | |
545 | if (connect(s, (struct sockaddr *) &S, sizeof(struct sockaddr_in)) < 0) { | |
546 | snprintf(buf, 1024, "connect: %s\n", xstrerror()); | |
547 | error_html(buf); | |
548 | return 1; | |
549 | } | |
7395afb8 | 550 | l = snprintf(buf, sizeof(buf), |
d23f4e1b | 551 | "GET cache_object://%s/%s HTTP/1.0\r\n" |
552 | "Accept: */*\r\n" | |
2ac76861 | 553 | "%s" /* Authentication info or nothing */ |
d23f4e1b | 554 | "\r\n", |
555 | req->hostname, | |
7395afb8 | 556 | req->action, |
557 | make_auth_header(req)); | |
d23f4e1b | 558 | write(s, buf, l); |
7395afb8 | 559 | debug(1) fprintf(stderr, "wrote request: '%s'\n", buf); |
d23f4e1b | 560 | return read_reply(s, req); |
090089c4 | 561 | } |
562 | ||
684c2720 | 563 | int |
8203a132 | 564 | main(int argc, char *argv[]) |
090089c4 | 565 | { |
d23f4e1b | 566 | char *s; |
567 | cachemgr_request *req; | |
429fdbec | 568 | safe_inet_addr("255.255.255.255", &no_addr); |
d1a43e28 | 569 | now = time(NULL); |
090089c4 | 570 | if ((s = strrchr(argv[0], '/'))) |
3c0117c9 | 571 | progname = xstrdup(s + 1); |
090089c4 | 572 | else |
3c0117c9 | 573 | progname = xstrdup(argv[0]); |
d23f4e1b | 574 | if ((s = getenv("SCRIPT_NAME")) != NULL) |
3c0117c9 | 575 | script_name = xstrdup(s); |
d23f4e1b | 576 | req = read_request(); |
577 | return process_request(req); | |
090089c4 | 578 | } |
579 | ||
d23f4e1b | 580 | static char * |
581 | read_post_request(void) | |
090089c4 | 582 | { |
d23f4e1b | 583 | char *s; |
584 | char *buf; | |
585 | int len; | |
586 | if ((s = getenv("REQUEST_METHOD")) == NULL) | |
587 | return NULL; | |
588 | if (0 != strcasecmp(s, "POST")) | |
589 | return NULL; | |
590 | if ((s = getenv("CONTENT_LENGTH")) == NULL) | |
591 | return NULL; | |
592 | if ((len = atoi(s)) <= 0) | |
593 | return NULL; | |
594 | buf = xmalloc(len + 1); | |
595 | fread(buf, len, 1, stdin); | |
596 | buf[len] = '\0'; | |
597 | return buf; | |
598 | } | |
090089c4 | 599 | |
d23f4e1b | 600 | static char * |
601 | read_get_request(void) | |
602 | { | |
603 | char *s; | |
604 | if ((s = getenv("QUERY_STRING")) == NULL) | |
605 | return NULL; | |
606 | return xstrdup(s); | |
607 | } | |
090089c4 | 608 | |
d23f4e1b | 609 | static cachemgr_request * |
610 | read_request(void) | |
611 | { | |
612 | char *buf; | |
613 | cachemgr_request *req; | |
614 | char *s; | |
615 | char *t; | |
616 | char *q; | |
617 | if ((buf = read_post_request()) != NULL) | |
618 | (void) 0; | |
619 | else if ((buf = read_get_request()) != NULL) | |
429fdbec | 620 | (void) 0; |
233794cd | 621 | else |
d23f4e1b | 622 | return NULL; |
623 | if (strlen(buf) == 0) | |
624 | return NULL; | |
625 | req = xcalloc(1, sizeof(cachemgr_request)); | |
626 | for (s = strtok(buf, "&"); s != NULL; s = strtok(NULL, "&")) { | |
627 | t = xstrdup(s); | |
628 | if ((q = strchr(t, '=')) == NULL) | |
629 | continue; | |
630 | *q++ = '\0'; | |
7395afb8 | 631 | if (0 == strcasecmp(t, "host") && strlen(q)) |
d23f4e1b | 632 | req->hostname = xstrdup(q); |
2ac76861 | 633 | else if (0 == strcasecmp(t, "port") && strlen(q)) |
d23f4e1b | 634 | req->port = atoi(q); |
2ac76861 | 635 | else if (0 == strcasecmp(t, "user_name") && strlen(q)) |
7395afb8 | 636 | req->user_name = xstrdup(q); |
2ac76861 | 637 | else if (0 == strcasecmp(t, "passwd") && strlen(q)) |
d23f4e1b | 638 | req->passwd = xstrdup(q); |
2ac76861 | 639 | else if (0 == strcasecmp(t, "auth") && strlen(q)) |
7395afb8 | 640 | req->pub_auth = xstrdup(q), decode_pub_auth(req); |
2ac76861 | 641 | else if (0 == strcasecmp(t, "operation")) |
d23f4e1b | 642 | req->action = xstrdup(q); |
643 | } | |
7395afb8 | 644 | make_pub_auth(req); |
645 | debug(1) fprintf(stderr, "cmgr: got req: host: '%s' port: %d uname: '%s' passwd: '%s' auth: '%s' oper: '%s'\n", | |
646 | 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 | 647 | return req; |
090089c4 | 648 | } |
7395afb8 | 649 | |
650 | ||
651 | /* Routines to support authentication */ | |
652 | ||
653 | /* | |
654 | * Encodes auth info into a "public" form. | |
655 | * Currently no powerful encryption is used. | |
656 | */ | |
657 | static void | |
2ac76861 | 658 | make_pub_auth(cachemgr_request * req) |
7395afb8 | 659 | { |
660 | static char buf[1024]; | |
661 | safe_free(req->pub_auth); | |
662 | debug(3) fprintf(stderr, "cmgr: encoding for pub...\n"); | |
7021844c | 663 | if (!req->passwd || !strlen(req->passwd)) |
7395afb8 | 664 | return; |
665 | /* host | time | user | passwd */ | |
666 | snprintf(buf, sizeof(buf), "%s|%d|%s|%s", | |
667 | req->hostname, | |
1afe05c5 | 668 | (int) now, |
7395afb8 | 669 | req->user_name ? req->user_name : "", |
670 | req->passwd); | |
671 | debug(3) fprintf(stderr, "cmgr: pre-encoded for pub: %s\n", buf); | |
672 | debug(3) fprintf(stderr, "cmgr: encoded: '%s'\n", base64_encode(buf)); | |
673 | req->pub_auth = xstrdup(base64_encode(buf)); | |
674 | } | |
675 | ||
676 | static void | |
2ac76861 | 677 | decode_pub_auth(cachemgr_request * req) |
7395afb8 | 678 | { |
679 | char *buf; | |
680 | const char *host_name; | |
681 | const char *time_str; | |
682 | const char *user_name; | |
683 | const char *passwd; | |
684 | ||
685 | debug(2) fprintf(stderr, "cmgr: decoding pub: '%s'\n", safe_str(req->pub_auth)); | |
686 | safe_free(req->passwd); | |
687 | if (!req->pub_auth || strlen(req->pub_auth) < 4 + strlen(safe_str(req->hostname))) | |
688 | return; | |
689 | buf = xstrdup(base64_decode(req->pub_auth)); | |
690 | debug(3) fprintf(stderr, "cmgr: length ok\n"); | |
691 | /* parse ( a lot of memory leaks, but that is cachemgr style :) */ | |
692 | if ((host_name = strtok(buf, "|")) == NULL) | |
693 | return; | |
694 | debug(3) fprintf(stderr, "cmgr: decoded host: '%s'\n", host_name); | |
695 | if ((time_str = strtok(NULL, "|")) == NULL) | |
696 | return; | |
2ac76861 | 697 | debug(3) fprintf(stderr, "cmgr: decoded time: '%s' (now: %d)\n", time_str, (int) now); |
7395afb8 | 698 | if ((user_name = strtok(NULL, "|")) == NULL) |
699 | return; | |
700 | debug(3) fprintf(stderr, "cmgr: decoded uname: '%s'\n", user_name); | |
701 | if ((passwd = strtok(NULL, "|")) == NULL) | |
702 | return; | |
703 | debug(2) fprintf(stderr, "cmgr: decoded passwd: '%s'\n", passwd); | |
704 | /* verify freshness and validity */ | |
705 | if (atoi(time_str) + passwd_ttl < now) | |
706 | return; | |
707 | if (strcasecmp(host_name, req->hostname)) | |
708 | return; | |
709 | debug(1) fprintf(stderr, "cmgr: verified auth. info.\n"); | |
710 | /* ok, accept */ | |
711 | xfree(req->user_name); | |
712 | req->user_name = xstrdup(user_name); | |
713 | req->passwd = xstrdup(passwd); | |
714 | xfree(buf); | |
715 | } | |
716 | ||
717 | static void | |
2ac76861 | 718 | reset_auth(cachemgr_request * req) |
7395afb8 | 719 | { |
720 | safe_free(req->passwd); | |
721 | safe_free(req->pub_auth); | |
722 | } | |
723 | ||
724 | static const char * | |
2ac76861 | 725 | make_auth_header(const cachemgr_request * req) |
7395afb8 | 726 | { |
727 | static char buf[1024]; | |
b6a2f15e | 728 | off_t l = 0; |
7395afb8 | 729 | const char *str64; |
2ac76861 | 730 | if (!req->passwd) |
7395afb8 | 731 | return ""; |
732 | ||
2ac76861 | 733 | snprintf(buf, sizeof(buf), "%s:%s", |
7395afb8 | 734 | req->user_name ? req->user_name : "", |
2ac76861 | 735 | req->passwd); |
7395afb8 | 736 | |
737 | str64 = base64_encode(buf); | |
b6a2f15e | 738 | l += snprintf(buf, sizeof(buf), "Authorization: Basic %s\r\n", str64); |
739 | l += snprintf(&buf[l], sizeof(buf) - l, | |
740 | "Proxy-Authorization: Basic %s\r\n", str64); | |
7395afb8 | 741 | return buf; |
742 | } |