]> git.ipfire.org Git - thirdparty/squid.git/blame - tools/cachemgr.cc
Source Format Enforcement (#763)
[thirdparty/squid.git] / tools / cachemgr.cc
CommitLineData
5d985213 1/*
f70aedc4 2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
d23f4e1b 3 *
5f623035
AJ
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
30a4f2a8 7 */
234967c9 8
f7f3304a 9#include "squid.h"
5cc4b155 10#include "base/CharacterSet.h"
77b1029d 11#include "base64.h"
055421ee 12#include "getfullhostname.h"
25f98340 13#include "html_quote.h"
055421ee 14#include "ip/Address.h"
1f1eee3f 15#include "MemBuf.h"
25f98340 16#include "rfc1123.h"
1fa9b1a7 17#include "rfc1738.h"
055421ee 18#include "util.h"
234967c9 19
074d6a40
AJ
20#include <cctype>
21#include <cerrno>
22#include <csignal>
23#include <cstring>
24#include <ctime>
30a4f2a8 25#if HAVE_UNISTD_H
234967c9 26#include <unistd.h>
30a4f2a8 27#endif
30a4f2a8 28#if HAVE_FCNTL_H
234967c9 29#include <fcntl.h>
30a4f2a8 30#endif
31#if HAVE_GRP_H
234967c9 32#include <grp.h>
30a4f2a8 33#endif
88738790 34#if HAVE_GNUMALLOC_H
35#include <gnumalloc.h>
482aa790 36#elif HAVE_MALLOC_H
234967c9 37#include <malloc.h>
983061ed 38#endif
30a4f2a8 39#if HAVE_MEMORY_H
234967c9 40#include <memory.h>
30a4f2a8 41#endif
489520a9 42#if HAVE_NETDB_H
234967c9 43#include <netdb.h>
30a4f2a8 44#endif
45#if HAVE_PWD_H
234967c9 46#include <pwd.h>
30a4f2a8 47#endif
30a4f2a8 48#if HAVE_SYS_PARAM_H
234967c9 49#include <sys/param.h>
30a4f2a8 50#endif
30a4f2a8 51#if HAVE_SYS_SOCKET_H
234967c9 52#include <sys/socket.h>
30a4f2a8 53#endif
54#if HAVE_NETINET_IN_H
234967c9 55#include <netinet/in.h>
30a4f2a8 56#endif
57#if HAVE_ARPA_INET_H
234967c9 58#include <arpa/inet.h>
30a4f2a8 59#endif
60#if HAVE_SYS_STAT_H
234967c9 61#include <sys/stat.h>
30a4f2a8 62#endif
63#if HAVE_SYS_UN_H
234967c9 64#include <sys/un.h>
30a4f2a8 65#endif
66#if HAVE_SYS_WAIT_H
234967c9 67#include <sys/wait.h>
30a4f2a8 68#endif
69#if HAVE_LIBC_H
70#include <libc.h>
71#endif
30a4f2a8 72#if HAVE_STRINGS_H
234967c9 73#include <strings.h>
74#endif
234967c9 75#if HAVE_BSTRING_H
76#include <bstring.h>
77#endif
30a4f2a8 78#if HAVE_CRYPT_H
234967c9 79#include <crypt.h>
80#endif
42ad37af 81#if HAVE_FNMATCH_H
e1381638 82extern "C" {
42ad37af 83#include <fnmatch.h>
7dcff512 84}
42ad37af 85#endif
090089c4 86
42ad37af 87#ifndef DEFAULT_CACHEMGR_CONFIG
b4ee1908 88#define DEFAULT_CACHEMGR_CONFIG "/etc/squid/cachemgr.conf"
42ad37af 89#endif
90
3afd7aae 91typedef struct {
42ad37af 92 char *server;
d23f4e1b 93 char *hostname;
94 int port;
95 char *action;
7395afb8 96 char *user_name;
d23f4e1b 97 char *passwd;
7395afb8 98 char *pub_auth;
3469551a
CT
99 char *workers;
100 char *processes;
2fadd50d 101} cachemgr_request;
090089c4 102
7395afb8 103/*
104 * Static variables and constants
105 */
f53969cc 106static const time_t passwd_ttl = 60 * 60 * 3; /* in sec */
0ee4272b 107static const char *script_name = "/cgi-bin/cachemgr.cgi";
0ee4272b 108static const char *progname = NULL;
d1a43e28 109static time_t now;
62e76326 110
d23f4e1b 111/*
112 * Function prototypes
113 */
7395afb8 114static const char *safe_str(const char *str);
a2c963ae 115static const char *xstrtok(char **str, char del);
f5b8bbc4 116static void print_trailer(void);
a2c963ae 117static void auth_html(const char *host, int port, const char *user_name);
d23f4e1b 118static void error_html(const char *msg);
7395afb8 119static char *menu_url(cachemgr_request * req, const char *action);
120static int parse_status_line(const char *sline, const char **statusStr);
d23f4e1b 121static cachemgr_request *read_request(void);
122static char *read_get_request(void);
123static char *read_post_request(void);
124
2ac76861 125static void make_pub_auth(cachemgr_request * req);
126static void decode_pub_auth(cachemgr_request * req);
127static void reset_auth(cachemgr_request * req);
128static const char *make_auth_header(const cachemgr_request * req);
7395afb8 129
42ad37af 130static int check_target_acl(const char *hostname, int port);
131
7aa9bb3e 132#if _SQUID_WINDOWS_
099a1791 133static int s_iInitCount = 0;
a760862a 134
099a1791 135int Win32SockInit(void)
136{
137 int iVersionRequested;
138 WSADATA wsaData;
139 int err;
140
141 if (s_iInitCount > 0) {
14942edd 142 ++s_iInitCount;
62e76326 143 return (0);
144 } else if (s_iInitCount < 0)
145 return (s_iInitCount);
099a1791 146
2f8abb64 147 /* s_iInitCount == 0. Do the initialization */
099a1791 148 iVersionRequested = MAKEWORD(2, 0);
62e76326 149
099a1791 150 err = WSAStartup((WORD) iVersionRequested, &wsaData);
62e76326 151
099a1791 152 if (err) {
62e76326 153 s_iInitCount = -1;
154 return (s_iInitCount);
099a1791 155 }
62e76326 156
099a1791 157 if (LOBYTE(wsaData.wVersion) != 2 ||
62e76326 158 HIBYTE(wsaData.wVersion) != 0) {
159 s_iInitCount = -2;
160 WSACleanup();
161 return (s_iInitCount);
099a1791 162 }
62e76326 163
14942edd 164 ++s_iInitCount;
099a1791 165 return (s_iInitCount);
166}
167
168void Win32SockCleanup(void)
169{
170 if (--s_iInitCount == 0)
62e76326 171 WSACleanup();
172
099a1791 173 return;
174}
62e76326 175
1191b93b 176#endif
7395afb8 177
2ac76861 178static const char *
179safe_str(const char *str)
7395afb8 180{
181 return str ? str : "";
182}
183
7021844c 184/* relaxed number format */
185static int
186is_number(const char *str)
187{
188 return strspn(str, "\t -+01234567890./\n") == strlen(str);
189}
190
a2c963ae 191static const char *
2ac76861 192xstrtok(char **str, char del)
7395afb8 193{
194 if (*str) {
62e76326 195 char *p = strchr(*str, del);
196 char *tok = *str;
197 int len;
198
199 if (p) {
200 *str = p + 1;
201 *p = '\0';
202 } else
203 *str = NULL;
204
205 /* trim */
206 len = strlen(tok);
207
208 while (len && xisspace(tok[len - 1]))
209 tok[--len] = '\0';
210
211 while (xisspace(*tok))
14942edd 212 ++tok;
62e76326 213
214 return tok;
7395afb8 215 } else
62e76326 216 return "";
7395afb8 217}
090089c4 218
5cc4b155 219bool
220hostname_check(const char *uri)
221{
222 static CharacterSet hostChars = CharacterSet("host",".:[]_") +
77b1029d 223 CharacterSet::ALPHA + CharacterSet::DIGIT;
5cc4b155 224
225 const auto limit = strlen(uri);
226 for (size_t i = 0; i < limit; i++) {
227 if (!hostChars[uri[i]]) {
77b1029d 228 return false;
5cc4b155 229 }
230 }
231 return true;
232}
233
24382924 234static void
8203a132 235print_trailer(void)
090089c4 236{
df339671 237 printf("<HR noshade size=\"1px\">\n");
090089c4 238 printf("<ADDRESS>\n");
239 printf("Generated %s, by %s/%s@%s\n",
62e76326 240 mkrfc1123(now), progname, VERSION, getfullhostname());
a8f7d3ee 241 printf("</ADDRESS></BODY></HTML>\n");
090089c4 242}
243
24382924 244static void
a2c963ae 245auth_html(const char *host, int port, const char *user_name)
090089c4 246{
42ad37af 247 FILE *fp;
248 int need_host = 1;
249
2ac76861 250 if (!user_name)
62e76326 251 user_name = "";
252
2ac76861 253 if (!host || !strlen(host))
42ad37af 254 host = "";
62e76326 255
b073fc4b
AJ
256 fp = fopen("cachemgr.conf", "r");
257
258 if (fp == NULL)
259 fp = fopen(DEFAULT_CACHEMGR_CONFIG, "r");
260
261 if (fp == NULL)
262 printf("X-Error: message=\"Unable to open config %s\"", DEFAULT_CACHEMGR_CONFIG);
263
df339671 264 printf("Content-Type: text/html\r\n\r\n");
62e76326 265
df339671 266 printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
62e76326 267
df339671 268 printf("<HTML><HEAD><TITLE>Cache Manager Interface</TITLE>\n");
62e76326 269
b073fc4b
AJ
270 printf("<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n");
271
272 printf("<script type=\"text/javascript\">\n");
273 printf("function TS(t, s) {\n");
274 printf(" var x = new XMLHttpRequest();\n");
275 printf(" x.open('GET', 'http' + s + '://' + t + '/squid-internal-mgr/', true);\n");
276 printf(" x.onreadystatechange=function() {\n");
277 printf(" if (x.readyState==4) {\n");
278 printf(" if ((x.status>=200 && x.status <= 299) || x.status==401) {\n");
279 printf(" var v = x.getResponseHeader('Server');\n");
280 printf(" if (v.substring(0,8) == 'squid/3.' && (v[8]=='H' || parseInt(v.substring(8)) >= 2)) {\n");
281 printf(" var d = document.getElementById('H' + s + 'mgr');\n");
282 printf(" if (d.innerHTML == '') d.innerHTML = '<h2>HTTP' + (s=='s'?'S':'') + ' Managed Proxies</h2>';\n");
283 printf(" d.innerHTML = d.innerHTML + '<p>Host: <a href=\"http' + s + '://' + t + '/squid-internal-mgr/\">' + t + '</a></p>';\n");
284 printf(" }}}}\n");
285 printf(" x.send(null);\n");
286 printf("}\n");
287 printf("</script>\n");
288
289 printf("</HEAD>\n");
62e76326 290
a8f7d3ee 291 printf("<BODY><H1>Cache Manager Interface</H1>\n");
62e76326 292
f7d9a2ad 293 printf("<P>This is a WWW interface to the instrumentation interface\n");
62e76326 294
f7d9a2ad 295 printf("for the Squid object cache.</P>\n");
62e76326 296
df339671 297 printf("<HR noshade size=\"1px\">\n");
62e76326 298
b073fc4b
AJ
299 printf("<div id=\"Hsmgr\"></div>\n");
300 printf("<div id=\"Hmgr\"></div>\n");
301 printf("<div id=\"Cmgr\">\n");
302 printf("<h2>CGI Managed Proxies</h2>\n");
f23f96e6 303 printf("<FORM METHOD=\"POST\" ACTION=\"%s\">\n", script_name);
62e76326 304
d980562f 305 printf("<TABLE BORDER=\"0\" CELLPADDING=\"10\" CELLSPACING=\"1\">\n");
62e76326 306
42ad37af 307 if (fp != NULL) {
308 int servers = 0;
309 char config_line[BUFSIZ];
310
311 while (fgets(config_line, BUFSIZ, fp)) {
312 char *server, *comment;
c202be8a
AJ
313 if (strtok(config_line, "\r\n") == nullptr)
314 continue;
42ad37af 315
316 if (config_line[0] == '#')
317 continue;
318
319 if (config_line[0] == '\0')
320 continue;
321
322 if ((server = strtok(config_line, " \t")) == NULL)
323 continue;
324
325 if (strchr(server, '*') || strchr(server, '[') || strchr(server, '?')) {
326 need_host = -1;
327 continue;
328 }
329
330 comment = strtok(NULL, "");
331
332 if (comment)
333 while (*comment == ' ' || *comment == '\t')
14942edd 334 ++comment;
42ad37af 335
336 if (!comment || !*comment)
337 comment = server;
338
b073fc4b
AJ
339 if (!servers)
340 printf("<TR><TH ALIGN=\"left\">Cache Server:</TH><TD><SELECT id=\"server\" NAME=\"server\">\n");
42ad37af 341
342 printf("<OPTION VALUE=\"%s\"%s>%s</OPTION>\n", server, (servers || *host) ? "" : " SELECTED", comment);
14942edd 343 ++servers;
42ad37af 344 }
345
346 if (servers) {
347 if (need_host == 1 && !*host)
348 need_host = 0;
349
350 if (need_host)
351 printf("<OPTION VALUE=\"\"%s>Other</OPTION>\n", (*host) ? " SELECTED" : "");
352
353 printf("</SELECT></TR>\n");
354 }
355
356 fclose(fp);
357 }
358
359 if (need_host) {
360 if (need_host == 1 && !*host)
361 host = "localhost";
362
363 printf("<TR><TH ALIGN=\"left\">Cache Host:</TH><TD><INPUT NAME=\"host\" ");
62e76326 364
42ad37af 365 printf("size=\"30\" VALUE=\"%s\"></TD></TR>\n", host);
62e76326 366
42ad37af 367 printf("<TR><TH ALIGN=\"left\">Cache Port:</TH><TD><INPUT NAME=\"port\" ");
368
369 printf("size=\"30\" VALUE=\"%d\"></TD></TR>\n", port);
370 }
62e76326 371
7395afb8 372 printf("<TR><TH ALIGN=\"left\">Manager name:</TH><TD><INPUT NAME=\"user_name\" ");
62e76326 373
2e29287d 374 printf("size=\"30\" VALUE=\"%s\"></TD></TR>\n", rfc1738_escape(user_name));
62e76326 375
7395afb8 376 printf("<TR><TH ALIGN=\"left\">Password:</TH><TD><INPUT TYPE=\"password\" NAME=\"passwd\" ");
62e76326 377
df339671 378 printf("size=\"30\" VALUE=\"\"></TD></TR>\n");
62e76326 379
7395afb8 380 printf("</TABLE><BR CLEAR=\"all\">\n");
62e76326 381
d23f4e1b 382 printf("<INPUT TYPE=\"submit\" VALUE=\"Continue...\">\n");
62e76326 383
b073fc4b
AJ
384 printf("</FORM></div>\n");
385
386 printf("<script type=\"text/javascript\">\n");
387 printf("var s = document.getElementById(\"server\");\n");
388 printf("for (var i = 0; i < s.childElementCount; i++) {\n");
389 printf(" TS(s.children[i].value, '');\n");
390 printf(" TS(s.children[i].value, 's');\n");
391 printf("}</script>\n");
62e76326 392
090089c4 393 print_trailer();
394}
395
d23f4e1b 396static void
397error_html(const char *msg)
090089c4 398{
df339671 399 printf("Content-Type: text/html\r\n\r\n");
400 printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
401 printf("<HTML><HEAD><TITLE>Cache Manager Error</TITLE>\n");
d8c0128e 402 printf("<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE></HEAD>\n");
d23f4e1b 403 printf("<BODY><H1>Cache Manager Error</H1>\n");
9a6a600f 404 printf("<P>\n%s</P>\n", html_quote(msg));
d23f4e1b 405 print_trailer();
090089c4 406}
407
7395afb8 408/* returns http status extracted from status line or -1 on parsing failure */
409static int
410parse_status_line(const char *sline, const char **statusStr)
411{
412 const char *sp = strchr(sline, ' ');
62e76326 413
7395afb8 414 if (statusStr)
62e76326 415 *statusStr = NULL;
416
7395afb8 417 if (strncasecmp(sline, "HTTP/", 5) || !sp)
62e76326 418 return -1;
419
3d0ac046 420 while (xisspace(*++sp));
b6a2f15e 421 if (!xisdigit(*sp))
62e76326 422 return -1;
423
7395afb8 424 if (statusStr)
62e76326 425 *statusStr = sp;
426
7395afb8 427 return atoi(sp);
428}
429
d23f4e1b 430static char *
7395afb8 431menu_url(cachemgr_request * req, const char *action)
090089c4 432{
d23f4e1b 433 static char url[1024];
137ee196 434 snprintf(url, sizeof(url), "%s?host=%s&port=%d&user_name=%s&operation=%s&auth=%s",
62e76326 435 script_name,
436 req->hostname,
437 req->port,
2e29287d 438 rfc1738_escape(safe_str(req->user_name)),
62e76326 439 action,
440 safe_str(req->pub_auth));
d23f4e1b 441 return url;
090089c4 442}
443
1f1eee3f
AJ
444static void
445munge_menu_line(MemBuf &out, const char *buf, cachemgr_request * req)
090089c4 446{
d23f4e1b 447 char *x;
7395afb8 448 const char *a;
449 const char *d;
450 const char *p;
451 char *a_url;
7021844c 452 char *buf_copy;
62e76326 453
1f1eee3f
AJ
454 const char bufLen = strlen(buf);
455 if (bufLen < 1 || *buf != ' ') {
456 out.append(buf, bufLen);
457 return;
458 }
62e76326 459
b995afba 460 buf_copy = x = xstrndup(buf, bufLen+1);
62e76326 461
7395afb8 462 a = xstrtok(&x, '\t');
62e76326 463
7395afb8 464 d = xstrtok(&x, '\t');
62e76326 465
7395afb8 466 p = xstrtok(&x, '\t');
62e76326 467
7395afb8 468 a_url = xstrdup(menu_url(req, a));
62e76326 469
7395afb8 470 /* no reason to give a url for a disabled action */
471 if (!strcmp(p, "disabled"))
1f1eee3f 472 out.appendf("<LI type=\"circle\">%s (disabled)<A HREF=\"%s\">.</A>\n", d, a_url);
7395afb8 473 else
62e76326 474 /* disable a hidden action (requires a password, but password is not in squid.conf) */
475 if (!strcmp(p, "hidden"))
1f1eee3f 476 out.appendf("<LI type=\"circle\">%s (hidden)<A HREF=\"%s\">.</A>\n", d, a_url);
62e76326 477 else
478 /* disable link if authentication is required and we have no password */
479 if (!strcmp(p, "protected") && !req->passwd)
1f1eee3f
AJ
480 out.appendf("<LI type=\"circle\">%s (requires <a href=\"%s\">authentication</a>)<A HREF=\"%s\">.</A>\n",
481 d, menu_url(req, "authenticate"), a_url);
62e76326 482 else
483 /* highlight protected but probably available entries */
484 if (!strcmp(p, "protected"))
1f1eee3f
AJ
485 out.appendf("<LI type=\"square\"><A HREF=\"%s\"><font color=\"#FF0000\">%s</font></A>\n",
486 a_url, d);
62e76326 487
7395afb8 488 /* public entry or unknown type of protection */
62e76326 489 else
1f1eee3f 490 out.appendf("<LI type=\"disk\"><A HREF=\"%s\">%s</A>\n", a_url, d);
62e76326 491
7395afb8 492 xfree(a_url);
62e76326 493
7021844c 494 xfree(buf_copy);
7021844c 495}
496
1f1eee3f
AJ
497static void
498munge_other_line(MemBuf &out, const char *buf, cachemgr_request *)
7021844c 499{
62e76326 500 static const char *ttags[] = {"td", "th"};
501
728da2ee 502 static int table_line_num = 0;
503 static int next_is_header = 0;
7021844c 504 int is_header = 0;
505 const char *ttag;
7021844c 506 char *buf_copy;
b9bc2838 507 char *x, *p;
7021844c 508 /* does it look like a table? */
62e76326 509
7021844c 510 if (!strchr(buf, '\t') || *buf == '\t') {
62e76326 511 /* nope, just text */
1f1eee3f
AJ
512 if (table_line_num)
513 out.append("</table>\n<pre>", 14);
514 out.appendf("%s", html_quote(buf));
62e76326 515 table_line_num = 0;
1f1eee3f 516 return;
7021844c 517 }
62e76326 518
b9bc2838 519 /* start html table */
520 if (!table_line_num) {
1f1eee3f 521 out.append("</pre><table cellpadding=\"2\" cellspacing=\"1\">\n", 46);
62e76326 522 next_is_header = 0;
b9bc2838 523 }
62e76326 524
b9bc2838 525 /* remove '\n' */
526 is_header = (!table_line_num || next_is_header) && !strchr(buf, ':') && !is_number(buf);
62e76326 527
7021844c 528 ttag = ttags[is_header];
62e76326 529
7021844c 530 /* record starts */
1f1eee3f 531 out.append("<tr>", 4);
62e76326 532
7021844c 533 /* substitute '\t' */
534 buf_copy = x = xstrdup(buf);
62e76326 535
399e85ea 536 if ((p = strchr(x, '\n')))
62e76326 537 *p = '\0';
538
b9bc2838 539 while (x && strlen(x)) {
62e76326 540 int column_span = 1;
541 const char *cell = xstrtok(&x, '\t');
542
543 while (x && *x == '\t') {
14942edd
FC
544 ++column_span;
545 ++x;
62e76326 546 }
547
1f1eee3f
AJ
548 out.appendf("<%s colspan=\"%d\" align=\"%s\">%s</%s>",
549 ttag, column_span,
550 is_header ? "center" : is_number(cell) ? "right" : "left",
551 html_quote(cell), ttag);
7021844c 552 }
62e76326 553
7021844c 554 xfree(buf_copy);
555 /* record ends */
1f1eee3f 556 out.append("</tr>\n", 6);
b9bc2838 557 next_is_header = is_header && strstr(buf, "\t\t");
14942edd 558 ++table_line_num;
090089c4 559}
560
e313f2b8
AJ
561static const char *
562munge_action_line(const char *_buf, cachemgr_request * req)
563{
564 static char html[2 * 1024];
565 char *buf = xstrdup(_buf);
566 char *x = buf;
567 const char *action, *description;
568 char *p;
569
570 if ((p = strchr(x, '\n')))
3afd7aae 571 *p = '\0';
e313f2b8 572 action = xstrtok(&x, '\t');
958caaf8
AJ
573 if (!action) {
574 xfree(buf);
575 return "";
576 }
e313f2b8
AJ
577 description = xstrtok(&x, '\t');
578 if (!description)
3afd7aae 579 description = action;
e313f2b8 580 snprintf(html, sizeof(html), " <a href=\"%s\">%s</a>", menu_url(req, action), description);
958caaf8 581 xfree(buf);
e313f2b8
AJ
582 return html;
583}
584
d23f4e1b 585static int
586read_reply(int s, cachemgr_request * req)
090089c4 587{
2ac76861 588 char buf[4 * 1024];
7aa9bb3e 589#if _SQUID_WINDOWS_
62e76326 590
099a1791 591 int reply;
a760862a 592 char *tmpfile = tempnam(NULL, "tmp0000");
593 FILE *fp = fopen(tmpfile, "w+");
099a1791 594#else
62e76326 595
d23f4e1b 596 FILE *fp = fdopen(s, "r");
099a1791 597#endif
7395afb8 598 /* interpretation states */
2ac76861 599 enum {
e313f2b8 600 isStatusLine, isHeaders, isActions, isBodyStart, isBody, isForward, isEof, isForwardEof, isSuccess, isError
2ac76861 601 } istate = isStatusLine;
d23f4e1b 602 int parse_menu = 0;
7395afb8 603 const char *action = req->action;
604 const char *statusStr = NULL;
605 int status = -1;
62e76326 606
d23f4e1b 607 if (0 == strlen(req->action))
62e76326 608 parse_menu = 1;
d23f4e1b 609 else if (0 == strcasecmp(req->action, "menu"))
62e76326 610 parse_menu = 1;
611
d23f4e1b 612 if (fp == NULL) {
7aa9bb3e 613#if _SQUID_WINDOWS_
a760862a 614 perror(tmpfile);
615 xfree(tmpfile);
616#else
bba20018 617
62e76326 618 perror("fdopen");
d52318df 619#endif
bba20018
AJ
620
621 close(s);
62e76326 622 return 1;
090089c4 623 }
62e76326 624
7aa9bb3e 625#if _SQUID_WINDOWS_
ad1fa9c9 626
9e167fa2 627 while ((reply=recv(s, buf, sizeof(buf), 0)) > 0)
ad1fa9c9 628 fwrite(buf, 1, reply, fp);
62e76326 629
099a1791 630 rewind(fp);
62e76326 631
099a1791 632#endif
62e76326 633
7395afb8 634 if (parse_menu)
62e76326 635 action = "menu";
636
7395afb8 637 /* read reply interpreting one line at a time depending on state */
638 while (istate < isEof) {
62e76326 639 if (!fgets(buf, sizeof(buf), fp))
640 istate = istate == isForward ? isForwardEof : isEof;
641
642 switch (istate) {
643
644 case isStatusLine:
645 /* get HTTP status */
646 /* uncomment the following if you want to debug headers */
647 /* fputs("\r\n\r\n", stdout); */
648 status = parse_status_line(buf, &statusStr);
649 istate = status == 200 ? isHeaders : isForward;
650 /* if cache asks for authentication, we have to reset our info */
651
652 if (status == 401 || status == 407) {
653 reset_auth(req);
2f8abb64 654 status = 403; /* Forbidden, see comments in case isForward: */
62e76326 655 }
656
657 /* this is a way to pass HTTP status to the Web server */
658 if (statusStr)
f53969cc 659 printf("Status: %d %s", status, statusStr); /* statusStr has '\n' */
62e76326 660
661 break;
662
663 case isHeaders:
664 /* forward header field */
f53969cc
SM
665 if (!strcmp(buf, "\r\n")) { /* end of headers */
666 fputs("Content-Type: text/html\r\n", stdout); /* add our type */
62e76326 667 istate = isBodyStart;
668 }
669
f53969cc 670 if (strncasecmp(buf, "Content-Type:", 13)) /* filter out their type */
62e76326 671 fputs(buf, stdout);
672
673 break;
674
675 case isBodyStart:
676 printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
677
678 printf("<HTML><HEAD><TITLE>CacheMgr@%s: %s</TITLE>\n",
679 req->hostname, action);
680
907e86e0 681 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 682
683 printf("</HEAD><BODY>\n");
684
685 if (parse_menu) {
686 printf("<H2><a href=\"%s\">Cache Manager</a> menu for %s:</H2>",
687 menu_url(req, "authenticate"), req->hostname);
688 printf("<UL>\n");
689 } else {
690 printf("<P><A HREF=\"%s\">%s</A>\n<HR noshade size=\"1px\">\n",
691 menu_url(req, "menu"), "Cache Manager menu");
692 printf("<PRE>\n");
693 }
694
e313f2b8 695 istate = isActions;
f53969cc 696 /* yes, fall through, we do not want to loose the first line */
e313f2b8
AJ
697
698 case isActions:
699 if (strncmp(buf, "action:", 7) == 0) {
700 fputs(" ", stdout);
701 fputs(munge_action_line(buf + 7, req), stdout);
702 break;
703 }
704 if (parse_menu) {
705 printf("<UL>\n");
706 } else {
707 printf("<HR noshade size=\"1px\">\n");
708 printf("<PRE>\n");
709 }
710
62e76326 711 istate = isBody;
f53969cc 712 /* yes, fall through, we do not want to loose the first line */
62e76326 713
714 case isBody:
1f1eee3f 715 {
62e76326 716 /* interpret [and reformat] cache response */
1f1eee3f
AJ
717 MemBuf out;
718 out.init();
62e76326 719 if (parse_menu)
1f1eee3f 720 munge_menu_line(out, buf, req);
62e76326 721 else
1f1eee3f 722 munge_other_line(out, buf, req);
62e76326 723
1f1eee3f
AJ
724 fputs(out.buf, stdout);
725 }
726 break;
62e76326 727
728 case isForward:
729 /* forward: no modifications allowed */
730 /*
731 * Note: we currently do not know any way to get browser.reply to
732 * 401 to .cgi because web server filters out all auth info. Thus we
733 * disable authentication headers for now.
734 */
f53969cc 735 if (!strncasecmp(buf, "WWW-Authenticate:", 17) || !strncasecmp(buf, "Proxy-Authenticate:", 19)); /* skip */
62e76326 736 else
737 fputs(buf, stdout);
738
739 break;
740
741 case isEof:
742 /* print trailers */
743 if (parse_menu)
744 printf("</UL>\n");
745 else
746 printf("</table></PRE>\n");
747
748 print_trailer();
749
750 istate = isSuccess;
751
752 break;
753
754 case isForwardEof:
755 /* indicate that we finished processing an "error" sequence */
756 istate = isError;
757
758 break;
759
760 default:
761 printf("%s: internal bug: invalid state reached: %d", script_name, istate);
762
763 istate = isError;
764 }
d23f4e1b 765 }
62e76326 766
4300768d 767 fclose(fp);
7aa9bb3e 768#if _SQUID_WINDOWS_
bba20018 769
a760862a 770 remove(tmpfile);
771 xfree(tmpfile);
772 close(s);
bba20018 773
a760862a 774#endif
775
d23f4e1b 776 return 0;
d1a43e28 777}
090089c4 778
d23f4e1b 779static int
780process_request(cachemgr_request * req)
090089c4 781{
62e76326 782
cc192b50 783 char ipbuf[MAX_IPSTRLEN];
784 struct addrinfo *AI = NULL;
b7ac5457 785 Ip::Address S;
d23f4e1b 786 int s;
787 int l;
62e76326 788
2ac76861 789 static char buf[2 * 1024];
62e76326 790
d23f4e1b 791 if (req == NULL) {
62e76326 792 auth_html(CACHEMGR_HOSTNAME, CACHE_HTTP_PORT, "");
793 return 1;
d23f4e1b 794 }
62e76326 795
d23f4e1b 796 if (req->hostname == NULL) {
62e76326 797 req->hostname = xstrdup(CACHEMGR_HOSTNAME);
d23f4e1b 798 }
62e76326 799
d23f4e1b 800 if (req->port == 0) {
62e76326 801 req->port = CACHE_HTTP_PORT;
d23f4e1b 802 }
62e76326 803
d23f4e1b 804 if (req->action == NULL) {
62e76326 805 req->action = xstrdup("");
d23f4e1b 806 }
62e76326 807
42ad37af 808 if (strcmp(req->action, "authenticate") == 0) {
62e76326 809 auth_html(req->hostname, req->port, req->user_name);
810 return 0;
7395afb8 811 }
62e76326 812
42ad37af 813 if (!check_target_acl(req->hostname, req->port)) {
958caaf8 814 snprintf(buf, sizeof(buf), "target %s:%d not allowed in cachemgr.conf\n", req->hostname, req->port);
42ad37af 815 error_html(buf);
816 return 1;
817 }
818
cc192b50 819 S = *gethostbyname(req->hostname);
62e76326 820
4dd643d5 821 if ( !S.isAnyAddr() ) {
cc192b50 822 (void) 0;
8df44716 823 } else if ((S = req->hostname))
62e76326 824 (void) 0;
d23f4e1b 825 else {
5cc4b155 826 if (hostname_check(req->hostname)) {
827 snprintf(buf, sizeof(buf), "Unknown Host: %s\n", req->hostname);
828 error_html(buf);
829 return 1;
830 } else {
831 snprintf(buf, sizeof(buf), "%s\n", "Invalid Hostname");
832 error_html(buf);
833 return 1;
834 }
d23f4e1b 835 }
62e76326 836
4dd643d5 837 S.port(req->port);
cc192b50 838
4dd643d5 839 S.getAddrInfo(AI);
62e76326 840
616f5a95
AJ
841#if USE_IPV6
842 if ((s = socket( AI->ai_family, SOCK_STREAM, 0)) < 0) {
843#else
844 if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
845#endif
b69e9ffa
AJ
846 int xerrno = errno;
847 snprintf(buf, sizeof(buf), "socket: %s\n", xstrerr(xerrno));
616f5a95 848 error_html(buf);
851614a8 849 Ip::Address::FreeAddr(AI);
616f5a95
AJ
850 return 1;
851 }
852
cc192b50 853 if (connect(s, AI->ai_addr, AI->ai_addrlen) < 0) {
b69e9ffa
AJ
854 int xerrno = errno;
855 snprintf(buf, sizeof(buf), "connect %s: %s\n", S.toUrl(ipbuf,MAX_IPSTRLEN), xstrerr(xerrno));
62e76326 856 error_html(buf);
851614a8 857 Ip::Address::FreeAddr(AI);
958caaf8 858 close(s);
62e76326 859 return 1;
d23f4e1b 860 }
62e76326 861
851614a8 862 Ip::Address::FreeAddr(AI);
cc192b50 863
7395afb8 864 l = snprintf(buf, sizeof(buf),
3469551a 865 "GET cache_object://%s/%s%s%s HTTP/1.0\r\n"
e4e635e1 866 "User-Agent: cachemgr.cgi/%s\r\n"
62e76326 867 "Accept: */*\r\n"
f53969cc 868 "%s" /* Authentication info or nothing */
62e76326 869 "\r\n",
870 req->hostname,
871 req->action,
3469551a
CT
872 req->workers? "?workers=" : (req->processes ? "?processes=" : ""),
873 req->workers? req->workers : (req->processes ? req->processes: ""),
e4e635e1 874 VERSION,
62e76326 875 make_auth_header(req));
5fbb6c94 876 if (write(s, buf, l) < 0) {
ec85ebda 877 fprintf(stderr,"ERROR: (%d) writing request: '%s'\n", errno, buf);
5fbb6c94 878 } else {
ec85ebda 879 debug("wrote request: '%s'\n", buf);
5fbb6c94 880 }
d23f4e1b 881 return read_reply(s, req);
090089c4 882}
883
684c2720 884int
e1381638
AJ
885main(int argc, char *argv[])
886{
d23f4e1b 887 char *s;
888 cachemgr_request *req;
62e76326 889
d1a43e28 890 now = time(NULL);
7aa9bb3e 891#if _SQUID_WINDOWS_
62e76326 892
099a1791 893 Win32SockInit();
a760862a 894 atexit(Win32SockCleanup);
ad1fa9c9 895 _setmode( _fileno( stdin ), _O_BINARY );
896 _setmode( _fileno( stdout ), _O_BINARY );
897 _fmode = _O_BINARY;
898
099a1791 899 if ((s = strrchr(argv[0], '\\')))
900#else
62e76326 901
090089c4 902 if ((s = strrchr(argv[0], '/')))
099a1791 903#endif
62e76326 904
905 progname = xstrdup(s + 1);
090089c4 906 else
62e76326 907 progname = xstrdup(argv[0]);
908
d23f4e1b 909 if ((s = getenv("SCRIPT_NAME")) != NULL)
62e76326 910 script_name = xstrdup(s);
911
f54f527e
AJ
912 char **args = argv;
913 while (argc > 1 && args[1][0] == '-') {
ec85ebda
AJ
914// const char *value = "";
915 char option = args[1][1];
916 switch (option) {
917 case 'd':
918 debug_enabled = 1;
919 break;
920 default:
921#if 0 // unused for now.
922 if (strlen(args[1]) > 2) {
923 value = args[1] + 2;
924 } else if (argc > 2) {
925 value = args[2];
14942edd 926 ++args;
5e263176 927 --argc;
ec85ebda
AJ
928 } else
929 value = "";
ec85ebda 930#endif
ded1ca0b 931 break;
ec85ebda 932 }
14942edd 933 ++args;
5e263176 934 --argc;
ec85ebda
AJ
935 }
936
d23f4e1b 937 req = read_request();
62e76326 938
d23f4e1b 939 return process_request(req);
090089c4 940}
941
d23f4e1b 942static char *
e1381638
AJ
943read_post_request(void)
944{
d23f4e1b 945 char *s;
62e76326 946
d23f4e1b 947 if ((s = getenv("REQUEST_METHOD")) == NULL)
62e76326 948 return NULL;
949
d23f4e1b 950 if (0 != strcasecmp(s, "POST"))
62e76326 951 return NULL;
952
d23f4e1b 953 if ((s = getenv("CONTENT_LENGTH")) == NULL)
62e76326 954 return NULL;
955
958caaf8 956 if (*s == '-') // negative length content huh?
62e76326 957 return NULL;
958
958caaf8 959 uint64_t len;
62e76326 960
958caaf8
AJ
961 char *endptr = s+ strlen(s);
962 if ((len = strtoll(s, &endptr, 10)) <= 0)
bdcd85c3 963 return NULL;
62e76326 964
958caaf8
AJ
965 // limit the input to something reasonable.
966 // 4KB should be enough for the GET/POST data length, but may be extended.
e4877287 967 size_t bufLen = (len < 4096 ? len : 4095);
958caaf8
AJ
968 char *buf = (char *)xmalloc(bufLen + 1);
969
e4877287 970 size_t readLen = fread(buf, 1, bufLen, stdin);
958caaf8
AJ
971 if (readLen == 0) {
972 xfree(buf);
973 return NULL;
974 }
975 buf[readLen] = '\0';
976 len -= readLen;
977
978 // purge the remainder of the request entity
4bc67ec8 979 while (len > 0 && readLen) {
958caaf8 980 char temp[65535];
e4877287 981 readLen = fread(temp, 1, 65535, stdin);
958caaf8
AJ
982 len -= readLen;
983 }
62e76326 984
d23f4e1b 985 return buf;
986}
090089c4 987
d23f4e1b 988static char *
e1381638
AJ
989read_get_request(void)
990{
d23f4e1b 991 char *s;
62e76326 992
d23f4e1b 993 if ((s = getenv("QUERY_STRING")) == NULL)
62e76326 994 return NULL;
995
d23f4e1b 996 return xstrdup(s);
997}
090089c4 998
099a1791 999static cachemgr_request *
e1381638
AJ
1000read_request(void)
1001{
d23f4e1b 1002 char *buf;
62e76326 1003
d23f4e1b 1004 cachemgr_request *req;
1005 char *s;
03336c67 1006 char *t = NULL;
d23f4e1b 1007 char *q;
62e76326 1008
d23f4e1b 1009 if ((buf = read_post_request()) != NULL)
62e76326 1010 (void) 0;
d23f4e1b 1011 else if ((buf = read_get_request()) != NULL)
62e76326 1012 (void) 0;
233794cd 1013 else
62e76326 1014 return NULL;
1015
7aa9bb3e 1016#if _SQUID_WINDOWS_
62e76326 1017
099a1791 1018 if (strlen(buf) == 0 || strlen(buf) == 4000)
1019#else
62e76326 1020
d23f4e1b 1021 if (strlen(buf) == 0)
099a1791 1022#endif
368520fc 1023 {
34ede744 1024 xfree(buf);
62e76326 1025 return NULL;
368520fc 1026 }
62e76326 1027
e6ccf245 1028 req = (cachemgr_request *)xcalloc(1, sizeof(cachemgr_request));
62e76326 1029
d23f4e1b 1030 for (s = strtok(buf, "&"); s != NULL; s = strtok(NULL, "&")) {
03336c67 1031 safe_free(t);
62e76326 1032 t = xstrdup(s);
1033
1034 if ((q = strchr(t, '=')) == NULL)
1035 continue;
1036
14942edd
FC
1037 *q = '\0';
1038 ++q;
62e76326 1039
42ad37af 1040 rfc1738_unescape(t);
1041
1042 rfc1738_unescape(q);
1043
a37d6070 1044 if (0 == strcmp(t, "server") && strlen(q))
42ad37af 1045 req->server = xstrdup(q);
a37d6070 1046 else if (0 == strcmp(t, "host") && strlen(q))
62e76326 1047 req->hostname = xstrdup(q);
a37d6070 1048 else if (0 == strcmp(t, "port") && strlen(q))
62e76326 1049 req->port = atoi(q);
a37d6070 1050 else if (0 == strcmp(t, "user_name") && strlen(q))
62e76326 1051 req->user_name = xstrdup(q);
a37d6070 1052 else if (0 == strcmp(t, "passwd") && strlen(q))
62e76326 1053 req->passwd = xstrdup(q);
a37d6070 1054 else if (0 == strcmp(t, "auth") && strlen(q))
62e76326 1055 req->pub_auth = xstrdup(q), decode_pub_auth(req);
a37d6070 1056 else if (0 == strcmp(t, "operation"))
62e76326 1057 req->action = xstrdup(q);
a37d6070 1058 else if (0 == strcmp(t, "workers") && strlen(q))
3469551a 1059 req->workers = xstrdup(q);
a37d6070 1060 else if (0 == strcmp(t, "processes") && strlen(q))
3469551a 1061 req->processes = xstrdup(q);
d23f4e1b 1062 }
03336c67 1063 safe_free(t);
62e76326 1064
42ad37af 1065 if (req->server && !req->hostname) {
1066 char *p;
1067 req->hostname = strtok(req->server, ":");
1068
1069 if ((p = strtok(NULL, ":")))
1070 req->port = atoi(p);
1071 }
1072
7395afb8 1073 make_pub_auth(req);
3469551a
CT
1074 debug("cmgr: got req: host: '%s' port: %d uname: '%s' passwd: '%s' auth: '%s' oper: '%s' workers: '%s' processes: '%s'\n",
1075 safe_str(req->hostname), req->port, safe_str(req->user_name), safe_str(req->passwd), safe_str(req->pub_auth), safe_str(req->action), safe_str(req->workers), safe_str(req->processes));
d23f4e1b 1076 return req;
090089c4 1077}
7395afb8 1078
7395afb8 1079/* Routines to support authentication */
1080
1081/*
3afd7aae 1082 * Encodes auth info into a "public" form.
7395afb8 1083 * Currently no powerful encryption is used.
1084 */
1085static void
e1381638
AJ
1086make_pub_auth(cachemgr_request * req)
1087{
7395afb8 1088 static char buf[1024];
1089 safe_free(req->pub_auth);
ec85ebda 1090 debug("cmgr: encoding for pub...\n");
62e76326 1091
7021844c 1092 if (!req->passwd || !strlen(req->passwd))
62e76326 1093 return;
1094
ec271fe8
Å B
1095 auto *rfc1738_username = xstrdup(rfc1738_escape(safe_str(req->user_name)));
1096 auto *rfc1738_passwd = xstrdup(rfc1738_escape(req->passwd));
1097
7395afb8 1098 /* host | time | user | passwd */
8bdd0cec
AJ
1099 const int bufLen = snprintf(buf, sizeof(buf), "%s|%d|%s|%s",
1100 req->hostname,
1101 (int) now,
ec271fe8
Å B
1102 rfc1738_username,
1103 rfc1738_passwd);
ec85ebda 1104 debug("cmgr: pre-encoded for pub: %s\n", buf);
62e76326 1105
ec271fe8
Å B
1106 safe_free(rfc1738_username);
1107 safe_free(rfc1738_passwd);
1108
8bdd0cec
AJ
1109 const int encodedLen = base64_encode_len(bufLen);
1110 req->pub_auth = (char *) xmalloc(encodedLen);
aadbbd7d
AJ
1111 struct base64_encode_ctx ctx;
1112 base64_encode_init(&ctx);
1d11e9b3
AJ
1113 size_t blen = base64_encode_update(&ctx, req->pub_auth, bufLen, reinterpret_cast<uint8_t*>(buf));
1114 blen += base64_encode_final(&ctx, req->pub_auth + blen);
aadbbd7d 1115 req->pub_auth[blen] = '\0';
8bdd0cec 1116 debug("cmgr: encoded: '%s'\n", req->pub_auth);
7395afb8 1117}
1118
1119static void
e1381638
AJ
1120decode_pub_auth(cachemgr_request * req)
1121{
7395afb8 1122 const char *host_name;
1123 const char *time_str;
7395afb8 1124
ec85ebda 1125 debug("cmgr: decoding pub: '%s'\n", safe_str(req->pub_auth));
7395afb8 1126 safe_free(req->passwd);
62e76326 1127
7395afb8 1128 if (!req->pub_auth || strlen(req->pub_auth) < 4 + strlen(safe_str(req->hostname)))
62e76326 1129 return;
1130
90661806 1131 char *buf = static_cast<char*>(xmalloc(BASE64_DECODE_LENGTH(strlen(req->pub_auth))+1));
aadbbd7d
AJ
1132 struct base64_decode_ctx ctx;
1133 base64_decode_init(&ctx);
90661806 1134 size_t decodedLen = 0;
1d11e9b3 1135 if (!base64_decode_update(&ctx, &decodedLen, reinterpret_cast<uint8_t*>(buf), strlen(req->pub_auth), req->pub_auth) ||
ccd727e0 1136 !base64_decode_final(&ctx)) {
aadbbd7d
AJ
1137 debug("cmgr: base64 decode failure. Incomplete auth token string.\n");
1138 xfree(buf);
1139 return;
1140 }
90661806 1141 buf[decodedLen] = '\0';
62e76326 1142
ec85ebda 1143 debug("cmgr: length ok\n");
62e76326 1144
7395afb8 1145 /* parse ( a lot of memory leaks, but that is cachemgr style :) */
90212e68
AJ
1146 if ((host_name = strtok(buf, "|")) == NULL) {
1147 xfree(buf);
62e76326 1148 return;
90212e68 1149 }
62e76326 1150
ec85ebda 1151 debug("cmgr: decoded host: '%s'\n", host_name);
62e76326 1152
90212e68
AJ
1153 if ((time_str = strtok(NULL, "|")) == NULL) {
1154 xfree(buf);
62e76326 1155 return;
90212e68 1156 }
62e76326 1157
ec85ebda 1158 debug("cmgr: decoded time: '%s' (now: %d)\n", time_str, (int) now);
62e76326 1159
2e29287d 1160 char *user_name;
90212e68
AJ
1161 if ((user_name = strtok(NULL, "|")) == NULL) {
1162 xfree(buf);
62e76326 1163 return;
90212e68 1164 }
2e29287d 1165 rfc1738_unescape(user_name);
62e76326 1166
ec85ebda 1167 debug("cmgr: decoded uname: '%s'\n", user_name);
62e76326 1168
2e29287d 1169 char *passwd;
90212e68
AJ
1170 if ((passwd = strtok(NULL, "|")) == NULL) {
1171 xfree(buf);
62e76326 1172 return;
90212e68 1173 }
2e29287d 1174 rfc1738_unescape(passwd);
62e76326 1175
ec85ebda 1176 debug("cmgr: decoded passwd: '%s'\n", passwd);
62e76326 1177
7395afb8 1178 /* verify freshness and validity */
90212e68
AJ
1179 if (atoi(time_str) + passwd_ttl < now) {
1180 xfree(buf);
62e76326 1181 return;
90212e68 1182 }
62e76326 1183
90212e68
AJ
1184 if (strcasecmp(host_name, req->hostname)) {
1185 xfree(buf);
62e76326 1186 return;
90212e68 1187 }
62e76326 1188
ec85ebda 1189 debug("cmgr: verified auth. info.\n");
62e76326 1190
7395afb8 1191 /* ok, accept */
90212e68 1192 safe_free(req->user_name);
62e76326 1193
7395afb8 1194 req->user_name = xstrdup(user_name);
62e76326 1195
7395afb8 1196 req->passwd = xstrdup(passwd);
62e76326 1197
7395afb8 1198 xfree(buf);
1199}
1200
1201static void
e1381638
AJ
1202reset_auth(cachemgr_request * req)
1203{
7395afb8 1204 safe_free(req->passwd);
1205 safe_free(req->pub_auth);
1206}
1207
1208static const char *
e1381638
AJ
1209make_auth_header(const cachemgr_request * req)
1210{
7395afb8 1211 static char buf[1024];
e6ccf245 1212 size_t stringLength = 0;
62e76326 1213
2ac76861 1214 if (!req->passwd)
62e76326 1215 return "";
7395afb8 1216
8bdd0cec
AJ
1217 int bufLen = snprintf(buf, sizeof(buf), "%s:%s",
1218 req->user_name ? req->user_name : "",
1219 req->passwd);
1220
1221 int encodedLen = base64_encode_len(bufLen);
1222 if (encodedLen <= 0)
1223 return "";
7395afb8 1224
1d11e9b3 1225 char *str64 = static_cast<char *>(xmalloc(encodedLen));
aadbbd7d
AJ
1226 struct base64_encode_ctx ctx;
1227 base64_encode_init(&ctx);
1228 size_t blen = base64_encode_update(&ctx, str64, bufLen, reinterpret_cast<uint8_t*>(buf));
1229 blen += base64_encode_final(&ctx, str64+blen);
1230 str64[blen] = '\0';
62e76326 1231
bb64d879 1232 stringLength += snprintf(buf, sizeof(buf), "Authorization: Basic %.*s\r\n", (int)blen, str64);
62e76326 1233
e6ccf245 1234 assert(stringLength < sizeof(buf));
62e76326 1235
bb64d879 1236 snprintf(&buf[stringLength], sizeof(buf) - stringLength, "Proxy-Authorization: Basic %.*s\r\n", (int)blen, str64);
62e76326 1237
95a59bc9 1238 xfree(str64);
7395afb8 1239 return buf;
1240}
42ad37af 1241
1242static int
e1381638
AJ
1243check_target_acl(const char *hostname, int port)
1244{
42ad37af 1245 char config_line[BUFSIZ];
1246 FILE *fp = NULL;
1247 int ret = 0;
1248 fp = fopen("cachemgr.conf", "r");
1249
1250 if (fp == NULL)
1251 fp = fopen(DEFAULT_CACHEMGR_CONFIG, "r");
1252
1253 if (fp == NULL) {
1254#ifdef CACHEMGR_HOSTNAME_DEFINED
1255
1256 if (strcmp(hostname, CACHEMGR_HOSTNAME) == 0 && port == CACHE_HTTP_PORT)
1257 return 1;
1258
1259#else
1260
1261 if (strcmp(hostname, "localhost") == 0)
1262 return 1;
1263
1264 if (strcmp(hostname, getfullhostname()) == 0)
1265 return 1;
1266
1267#endif
1268
1269 return 0;
1270 }
1271
1272 while (fgets(config_line, BUFSIZ, fp)) {
1273 char *token = NULL;
1274 strtok(config_line, " \r\n\t");
1275
1276 if (config_line[0] == '#')
1277 continue;
1278
1279 if (config_line[0] == '\0')
1280 continue;
1281
1282 if ((token = strtok(config_line, ":")) == NULL)
1283 continue;
1284
1285#if HAVE_FNMATCH_H
1286
1287 if (fnmatch(token, hostname, 0) != 0)
1288 continue;
1289
1290#else
1291
1292 if (strcmp(token, hostname) != 0)
1293 continue;
1294
1295#endif
1296
1297 if ((token = strtok(NULL, ":")) != NULL) {
1298 int i;
1299
b4ee1908 1300 if (strcmp(token, "*") == 0)
1301
1302 ; /* Wildcard port specification */
a37d6070 1303 else if (strcmp(token, "any") == 0)
b4ee1908 1304
1305 ; /* Wildcard port specification */
1306 else if (sscanf(token, "%d", &i) != 1)
42ad37af 1307 continue;
1308
b4ee1908 1309 else if (i != port)
42ad37af 1310 continue;
1311 } else if (port != CACHE_HTTP_PORT)
1312 continue;
1313
1314 ret = 1;
1315
1316 break;
1317 }
1318
1319 fclose(fp);
1320 return ret;
1321}
f53969cc 1322