52#include <sys/socket.h>
55#include <netinet/in.h>
87#ifndef DEFAULT_CACHEMGR_CONFIG
88#define DEFAULT_CACHEMGR_CONFIG "/etc/squid/cachemgr.conf"
114static const char *
safe_str(
const char *str);
115static const char *
xstrtok(
char **str,
char del);
117static void auth_html(
const char *host,
int port,
const char *user_name);
137 int iVersionRequested;
148 iVersionRequested = MAKEWORD(2, 0);
150 err = WSAStartup((
WORD) iVersionRequested, &wsaData);
157 if (LOBYTE(wsaData.wVersion) != 2 ||
158 HIBYTE(wsaData.wVersion) != 0) {
181 return str ? str :
"";
188 return strspn(str,
"\t -+01234567890./\n") == strlen(str);
195 char *p = strchr(*str, del);
225 const auto limit = strlen(uri);
226 for (
size_t i = 0; i < limit; i++) {
227 if (!hostChars[uri[i]]) {
237 printf(
"<HR noshade size=\"1px\">\n");
238 printf(
"<ADDRESS>\n");
239 printf(
"Generated %s, by %s/%s@%s\n",
241 printf(
"</ADDRESS></BODY></HTML>\n");
253 if (!host || !strlen(host))
256 fp = fopen(
"cachemgr.conf",
"r");
264 printf(
"Content-Type: text/html\r\n\r\n");
266 printf(
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
268 printf(
"<HTML><HEAD><TITLE>Cache Manager Interface</TITLE>\n");
270 printf(
"<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n");
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,6) == 'squid/' || v == 'squid') {\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(
" var sv = document.getElementById('server');\n");
285 printf(
" var op = sv.getElementsByTagName('OPTION');\n");
286 printf(
" for(var i=0; i<op.length; i++) { if (op[i].innerHTML == t) { sv.removeChild(op[i]); i--; }}\n");
287 printf(
" if (sv.getElementsByTagName('OPTION').length == 0) {\n");
288 printf(
" document.getElementById('Cmgr').innerHTML = '';\n");
290 printf(
" x.send(null);\n");
292 printf(
"</script>\n");
296 printf(
"<BODY><H1>Cache Manager Interface</H1>\n");
298 printf(
"<P>This is a WWW interface to the instrumentation interface\n");
300 printf(
"for the Squid object cache.</P>\n");
302 printf(
"<HR noshade size=\"1px\">\n");
304 printf(
"<div id=\"Hsmgr\"></div>\n");
305 printf(
"<div id=\"Hmgr\"></div>\n");
306 printf(
"<div id=\"Cmgr\">\n");
307 printf(
"<h2>CGI Managed Proxies</h2>\n");
308 printf(
"<FORM METHOD=\"POST\" ACTION=\"%s\">\n",
script_name);
310 printf(
"<TABLE BORDER=\"0\" CELLPADDING=\"10\" CELLSPACING=\"1\">\n");
316 while (fgets(config_line,
BUFSIZ, fp)) {
318 if (strtok(config_line,
"\r\n") ==
nullptr)
321 if (config_line[0] ==
'#')
324 if (config_line[0] ==
'\0')
327 if ((
server = strtok(config_line,
" \t")) ==
nullptr)
335 comment = strtok(
nullptr,
"");
338 while (*comment ==
' ' || *comment ==
'\t')
341 if (!comment || !*comment)
345 printf(
"<TR><TH ALIGN=\"left\">Cache Server:</TH><TD><SELECT id=\"server\" NAME=\"server\">\n");
347 printf(
"<OPTION VALUE=\"%s\"%s>%s</OPTION>\n",
server, (servers || *host) ?
"" :
" SELECTED", comment);
352 if (need_host == 1 && !*host)
356 printf(
"<OPTION VALUE=\"\"%s>Other</OPTION>\n", (*host) ?
" SELECTED" :
"");
358 printf(
"</SELECT></TR>\n");
365 if (need_host == 1 && !*host)
368 printf(
"<TR><TH ALIGN=\"left\">Cache Host:</TH><TD><INPUT NAME=\"host\" ");
370 printf(
"size=\"30\" VALUE=\"%s\"></TD></TR>\n", host);
372 printf(
"<TR><TH ALIGN=\"left\">Cache Port:</TH><TD><INPUT NAME=\"port\" ");
374 printf(
"size=\"30\" VALUE=\"%d\"></TD></TR>\n",
port);
377 printf(
"<TR><TH ALIGN=\"left\">Manager name:</TH><TD><INPUT NAME=\"user_name\" ");
379 printf(
"size=\"30\" VALUE=\"%s\"></TD></TR>\n",
rfc1738_escape(user_name));
381 printf(
"<TR><TH ALIGN=\"left\">Password:</TH><TD><INPUT TYPE=\"password\" NAME=\"passwd\" ");
383 printf(
"size=\"30\" VALUE=\"\"></TD></TR>\n");
385 printf(
"</TABLE><BR CLEAR=\"all\">\n");
387 printf(
"<INPUT TYPE=\"submit\" VALUE=\"Continue...\">\n");
389 printf(
"</FORM></div>\n");
391 printf(
"<script type=\"text/javascript\">\n");
392 printf(
"var s = document.getElementById(\"server\");\n");
393 printf(
"for (var i = 0; i < s.childElementCount; i++) {\n");
394 printf(
" TS(s.children[i].value, '');\n");
395 printf(
" TS(s.children[i].value, 's');\n");
396 printf(
"}</script>\n");
404 printf(
"Content-Type: text/html\r\n\r\n");
405 printf(
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
406 printf(
"<HTML><HEAD><TITLE>Cache Manager Error</TITLE>\n");
407 printf(
"<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE></HEAD>\n");
408 printf(
"<BODY><H1>Cache Manager Error</H1>\n");
417 const char *
sp = strchr(sline,
' ');
420 *statusStr =
nullptr;
422 if (strncasecmp(sline,
"HTTP/", 5) || !
sp)
438 static char url[1024];
439 snprintf(url,
sizeof(url),
"%s?host=%s&port=%d&user_name=%s&operation=%s&auth=%s",
459 const auto bufLen = strlen(buf);
468 buf_copy = x =
xstrndup(buf, bufLen+1);
479 if (!strcmp(p,
"disabled"))
480 out.
appendf(
"<LI type=\"circle\">%s (disabled)<A HREF=\"%s\">.</A>\n", d, a_url);
483 if (!strcmp(p,
"hidden"))
484 out.
appendf(
"<LI type=\"circle\">%s (hidden)<A HREF=\"%s\">.</A>\n", d, a_url);
487 if (!strcmp(p,
"protected") && !req->
passwd)
488 out.
appendf(
"<LI type=\"circle\">%s (requires <a href=\"%s\">authentication</a>)<A HREF=\"%s\">.</A>\n",
489 d,
menu_url(req,
"authenticate"), a_url);
492 if (!strcmp(p,
"protected"))
493 out.
appendf(
"<LI type=\"square\"><A HREF=\"%s\"><font color=\"#FF0000\">%s</font></A>\n",
498 out.
appendf(
"<LI type=\"disk\"><A HREF=\"%s\">%s</A>\n", a_url, d);
508 static const char *ttags[] = {
"td",
"th"};
510 static int table_line_num = 0;
511 static int next_is_header = 0;
518 if (!strchr(buf,
'\t') || *buf ==
'\t') {
521 out.
append(
"</table>\n<pre>", 14);
528 if (!table_line_num) {
529 out.
append(
"</pre><table cellpadding=\"2\" cellspacing=\"1\">\n", 46);
534 is_header = (!table_line_num || next_is_header) && !strchr(buf,
':') && !
is_number(buf);
536 ttag = ttags[is_header];
544 if ((p = strchr(x,
'\n')))
547 while (x && strlen(x)) {
549 const char *cell =
xstrtok(&x,
'\t');
551 while (x && *x ==
'\t') {
556 out.
appendf(
"<%s colspan=\"%d\" align=\"%s\">%s</%s>",
558 is_header ?
"center" :
is_number(cell) ?
"right" :
"left",
565 next_is_header = is_header && strstr(buf,
"\t\t");
572 static char html[2 * 1024];
575 const char *
action, *description;
578 if ((p = strchr(x,
'\n')))
585 description =
xstrtok(&x,
'\t');
588 snprintf(html,
sizeof(html),
" <a href=\"%s\">%s</a>",
menu_url(req,
action), description);
600 char *tmpfile =
tempnam(
nullptr,
"tmp0000");
601 FILE *fp = fopen(tmpfile,
"w+");
604 FILE *fp = fdopen(s,
"r");
608 isStatusLine, isHeaders, isActions, isBodyStart, isBody, isForward, isEof, isForwardEof, isSuccess, isError
609 } istate = isStatusLine;
612 const char *statusStr =
nullptr;
615 if (0 == strlen(req->
action))
617 else if (0 == strcasecmp(req->
action,
"menu"))
635 while ((reply=recv(s, buf,
sizeof(buf), 0)) > 0)
636 fwrite(buf, 1, reply, fp);
646 while (istate < isEof) {
647 if (!fgets(buf,
sizeof(buf), fp))
648 istate = istate == isForward ? isForwardEof : isEof;
657 istate = status == 200 ? isHeaders : isForward;
660 if (status == 401 || status == 407) {
667 printf(
"Status: %d %s", status, statusStr);
673 if (!strcmp(buf,
"\r\n")) {
674 fputs(
"Content-Type: text/html\r\n", stdout);
675 istate = isBodyStart;
678 if (strncasecmp(buf,
"Content-Type:", 13))
684 printf(
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
686 printf(
"<HTML><HEAD><TITLE>CacheMgr@%s: %s</TITLE>\n",
689 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");
691 printf(
"</HEAD><BODY>\n");
694 printf(
"<H2><a href=\"%s\">Cache Manager</a> menu for %s:</H2>",
698 printf(
"<P><A HREF=\"%s\">%s</A>\n<HR noshade size=\"1px\">\n",
699 menu_url(req,
"menu"),
"Cache Manager menu");
707 if (strncmp(buf,
"action:", 7) == 0) {
715 printf(
"<HR noshade size=\"1px\">\n");
732 fputs(out.
buf, stdout);
743 if (!strncasecmp(buf,
"WWW-Authenticate:", 17) || !strncasecmp(buf,
"Proxy-Authenticate:", 19));
754 printf(
"</table></PRE>\n");
769 printf(
"%s: internal bug: invalid state reached: %d",
script_name, istate);
792 struct addrinfo *AI =
nullptr;
797 static char buf[2 * 1024];
799 if (req ==
nullptr) {
808 if (req->
port == 0) {
812 if (req->
action ==
nullptr) {
816 if (strcmp(req->
action,
"authenticate") == 0) {
822 snprintf(buf,
sizeof(buf),
"target %s:%d not allowed in cachemgr.conf\n", req->
hostname, req->
port);
835 snprintf(buf,
sizeof(buf),
"Unknown Host: %s\n", req->
hostname);
839 snprintf(buf,
sizeof(buf),
"%s\n",
"Invalid Hostname");
850 if ((s = socket( AI->ai_family, SOCK_STREAM, 0)) < 0) {
852 if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
855 snprintf(buf,
sizeof(buf),
"socket: %s\n",
xstrerr(xerrno));
861 if (connect(s, AI->ai_addr, AI->ai_addrlen) < 0) {
875 l = snprintf(buf,
sizeof(buf),
876 "GET /squid-internal-mgr/%s%s%s HTTP/1.0\r\n"
878 "User-Agent: cachemgr.cgi/%s\r\n"
888 if (write(s, buf, l) < 0) {
889 fprintf(stderr,
"ERROR: (%d) writing request: '%s'\n", errno, buf);
891 debug(
"wrote request: '%s'\n", buf);
907 _setmode( _fileno( stdin ), _O_BINARY );
908 _setmode( _fileno( stdout ), _O_BINARY );
911 if ((s = strrchr(argv[0],
'\\')))
914 if ((s = strrchr(argv[0],
'/')))
921 if ((s = getenv(
"SCRIPT_NAME")) !=
nullptr)
925 while (argc > 1 && args[1][0] ==
'-') {
926 char option = args[1][1];
948 if ((s = getenv(
"REQUEST_METHOD")) ==
nullptr)
951 if (0 != strcasecmp(s,
"POST"))
954 if ((s = getenv(
"CONTENT_LENGTH")) ==
nullptr)
962 char *endptr = s+ strlen(s);
963 if ((len =
strtoll(s, &endptr, 10)) <= 0)
968 size_t bufLen = (len < 4096 ? len : 4095);
969 char *buf = (
char *)
xmalloc(bufLen + 1);
971 size_t readLen = fread(buf, 1, bufLen, stdin);
980 while (len > 0 && readLen) {
982 readLen = fread(temp, 1, 65535, stdin);
994 if ((s = getenv(
"QUERY_STRING")) ==
nullptr)
1019 if (strlen(buf) == 0 || strlen(buf) == 4000)
1022 if (strlen(buf) == 0)
1031 for (s = strtok(buf,
"&"); s !=
nullptr; s = strtok(
nullptr,
"&")) {
1035 if ((q = strchr(t,
'=')) ==
nullptr)
1045 if (0 == strcmp(t,
"server") && strlen(q))
1047 else if (0 == strcmp(t,
"host") && strlen(q))
1049 else if (0 == strcmp(t,
"port") && strlen(q))
1050 req->
port = atoi(q);
1051 else if (0 == strcmp(t,
"user_name") && strlen(q))
1053 else if (0 == strcmp(t,
"passwd") && strlen(q))
1055 else if (0 == strcmp(t,
"auth") && strlen(q))
1057 else if (0 == strcmp(t,
"operation"))
1059 else if (0 == strcmp(t,
"workers") && strlen(q))
1061 else if (0 == strcmp(t,
"processes") && strlen(q))
1070 if ((p = strtok(
nullptr,
":")))
1071 req->
port = atoi(p);
1075 debug(
"cmgr: got req: host: '%s' port: %d uname: '%s' passwd: '%s' auth: '%s' oper: '%s' workers: '%s' processes: '%s'\n",
1089 static char buf[1024];
1091 debug(
"cmgr: encoding for pub...\n");
1100 const int bufLen = snprintf(buf,
sizeof(buf),
"%s|%d|%s|%s",
1105 debug(
"cmgr: pre-encoded for pub: %s\n", buf);
1123 const char *host_name;
1124 const char *time_str;
1135 size_t decodedLen = 0;
1138 debug(
"cmgr: base64 decode failure. Incomplete auth token string.\n");
1142 buf[decodedLen] =
'\0';
1144 debug(
"cmgr: length ok\n");
1147 if ((host_name = strtok(buf,
"|")) ==
nullptr) {
1152 debug(
"cmgr: decoded host: '%s'\n", host_name);
1154 if ((time_str = strtok(
nullptr,
"|")) ==
nullptr) {
1159 debug(
"cmgr: decoded time: '%s' (now: %d)\n", time_str, (
int)
now);
1162 if ((user_name = strtok(
nullptr,
"|")) ==
nullptr) {
1168 debug(
"cmgr: decoded uname: '%s'\n", user_name);
1171 if ((passwd = strtok(
nullptr,
"|")) ==
nullptr) {
1177 debug(
"cmgr: decoded passwd: '%s'\n", passwd);
1185 if (strcasecmp(host_name, req->
hostname)) {
1190 debug(
"cmgr: verified auth. info.\n");
1212 static char buf[1024];
1213 size_t stringLength = 0;
1218 int bufLen = snprintf(buf,
sizeof(buf),
"%s:%s",
1223 if (encodedLen <= 0)
1226 char *str64 =
static_cast<char *
>(
xmalloc(encodedLen));
1233 stringLength += snprintf(buf,
sizeof(buf),
"Authorization: Basic %.*s\r\n", (
int)blen, str64);
1235 assert(stringLength <
sizeof(buf));
1237 snprintf(&buf[stringLength],
sizeof(buf) - stringLength,
"Proxy-Authorization: Basic %.*s\r\n", (
int)blen, str64);
1246 char config_line[
BUFSIZ];
1249 fp = fopen(
"cachemgr.conf",
"r");
1254 if (fp ==
nullptr) {
1255#ifdef CACHEMGR_HOSTNAME_DEFINED
1262 if (strcmp(hostname,
"localhost") == 0)
1273 while (fgets(config_line,
BUFSIZ, fp)) {
1274 char *token =
nullptr;
1275 strtok(config_line,
" \r\n\t");
1277 if (config_line[0] ==
'#')
1280 if (config_line[0] ==
'\0')
1283 if ((token = strtok(config_line,
":")) ==
nullptr)
1288 if (fnmatch(token, hostname, 0) != 0)
1293 if (strcmp(token, hostname) != 0)
1298 if ((token = strtok(
nullptr,
":")) !=
nullptr) {
1301 if (strcmp(token,
"*") == 0)
1304 else if (strcmp(token,
"any") == 0)
1307 else if (sscanf(token,
"%d", &i) != 1)
static int Win32SockInit(void)
static void Win32SockCleanup(void)
void base64_encode_init(struct base64_encode_ctx *ctx)
size_t base64_encode_update(struct base64_encode_ctx *ctx, char *dst, size_t length, const uint8_t *src)
void base64_decode_init(struct base64_decode_ctx *ctx)
size_t base64_encode_final(struct base64_encode_ctx *ctx, char *dst)
#define base64_encode_len(length)
int base64_decode_update(struct base64_decode_ctx *ctx, size_t *dst_length, uint8_t *dst, size_t src_length, const char *src)
int base64_decode_final(struct base64_decode_ctx *ctx)
#define BASE64_DECODE_LENGTH(length)
static char server[MAXLINE]
static char * read_post_request(void)
static const char * munge_action_line(const char *_buf, cachemgr_request *req)
int main(int argc, char *argv[])
static const char * xstrtok(char **str, char del)
static void munge_other_line(MemBuf &out, const char *buf, cachemgr_request *)
static char * menu_url(cachemgr_request *req, const char *action)
static void error_html(const char *msg)
static bool hostname_check(const char *uri)
static const char * script_name
static const time_t passwd_ttl
static const char * make_auth_header(const cachemgr_request *req)
static void decode_pub_auth(cachemgr_request *req)
static void print_trailer(void)
static void auth_html(const char *host, int port, const char *user_name)
static void reset_auth(cachemgr_request *req)
static int check_target_acl(const char *hostname, int port)
static int is_number(const char *str)
static void make_pub_auth(cachemgr_request *req)
static int parse_status_line(const char *sline, const char **statusStr)
static cachemgr_request * read_request(void)
#define DEFAULT_CACHEMGR_CONFIG
static const char * progname
static void munge_menu_line(MemBuf &out, const char *buf, cachemgr_request *req)
static int read_reply(int s, cachemgr_request *req)
static int process_request(cachemgr_request *req)
static const char * safe_str(const char *str)
static char * read_get_request(void)
optimized set of C chars, with quick membership test and merge support
static const CharacterSet DIGIT
static const CharacterSet ALPHA
static void FreeAddr(struct addrinfo *&ai)
void getAddrInfo(struct addrinfo *&ai, int force=AF_UNSPEC) const
char * toUrl(char *buf, unsigned int len) const
unsigned short port() const
void append(const char *c, int sz) override
void init(mb_size_t szInit, mb_size_t szMax)
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
void debug(const char *format,...)
SQUIDCEXTERN const char * getfullhostname(void)
char * html_quote(const char *string)
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
const char * FormatRfc1123(time_t)
static bool action(int fd, size_t metasize, const char *fn, const char *url, const SquidMetaList &meta)
#define rfc1738_escape(x)
void rfc1738_unescape(char *url)
#define CACHEMGR_HOSTNAME
int64_t strtoll(const char *nptr, char **endptr, int base)
char * tempnam(const char *dir, const char *pfx)
void * xcalloc(size_t n, size_t sz)
const char * xstrerr(int error)
char * xstrndup(const char *s, size_t n)