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,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");
285 printf(
" x.send(null);\n");
287 printf(
"</script>\n");
291 printf(
"<BODY><H1>Cache Manager Interface</H1>\n");
293 printf(
"<P>This is a WWW interface to the instrumentation interface\n");
295 printf(
"for the Squid object cache.</P>\n");
297 printf(
"<HR noshade size=\"1px\">\n");
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");
303 printf(
"<FORM METHOD=\"POST\" ACTION=\"%s\">\n",
script_name);
305 printf(
"<TABLE BORDER=\"0\" CELLPADDING=\"10\" CELLSPACING=\"1\">\n");
311 while (fgets(config_line,
BUFSIZ, fp)) {
313 if (strtok(config_line,
"\r\n") ==
nullptr)
316 if (config_line[0] ==
'#')
319 if (config_line[0] ==
'\0')
322 if ((
server = strtok(config_line,
" \t")) ==
nullptr)
330 comment = strtok(
nullptr,
"");
333 while (*comment ==
' ' || *comment ==
'\t')
336 if (!comment || !*comment)
340 printf(
"<TR><TH ALIGN=\"left\">Cache Server:</TH><TD><SELECT id=\"server\" NAME=\"server\">\n");
342 printf(
"<OPTION VALUE=\"%s\"%s>%s</OPTION>\n",
server, (servers || *host) ?
"" :
" SELECTED", comment);
347 if (need_host == 1 && !*host)
351 printf(
"<OPTION VALUE=\"\"%s>Other</OPTION>\n", (*host) ?
" SELECTED" :
"");
353 printf(
"</SELECT></TR>\n");
360 if (need_host == 1 && !*host)
363 printf(
"<TR><TH ALIGN=\"left\">Cache Host:</TH><TD><INPUT NAME=\"host\" ");
365 printf(
"size=\"30\" VALUE=\"%s\"></TD></TR>\n", host);
367 printf(
"<TR><TH ALIGN=\"left\">Cache Port:</TH><TD><INPUT NAME=\"port\" ");
369 printf(
"size=\"30\" VALUE=\"%d\"></TD></TR>\n",
port);
372 printf(
"<TR><TH ALIGN=\"left\">Manager name:</TH><TD><INPUT NAME=\"user_name\" ");
374 printf(
"size=\"30\" VALUE=\"%s\"></TD></TR>\n",
rfc1738_escape(user_name));
376 printf(
"<TR><TH ALIGN=\"left\">Password:</TH><TD><INPUT TYPE=\"password\" NAME=\"passwd\" ");
378 printf(
"size=\"30\" VALUE=\"\"></TD></TR>\n");
380 printf(
"</TABLE><BR CLEAR=\"all\">\n");
382 printf(
"<INPUT TYPE=\"submit\" VALUE=\"Continue...\">\n");
384 printf(
"</FORM></div>\n");
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");
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");
402 printf(
"<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE></HEAD>\n");
403 printf(
"<BODY><H1>Cache Manager Error</H1>\n");
412 const char *
sp = strchr(sline,
' ');
415 *statusStr =
nullptr;
417 if (strncasecmp(sline,
"HTTP/", 5) || !
sp)
433 static char url[1024];
434 snprintf(url,
sizeof(url),
"%s?host=%s&port=%d&user_name=%s&operation=%s&auth=%s",
454 const char bufLen = strlen(buf);
455 if (bufLen < 1 || *buf !=
' ') {
460 buf_copy = x =
xstrndup(buf, bufLen+1);
471 if (!strcmp(p,
"disabled"))
472 out.
appendf(
"<LI type=\"circle\">%s (disabled)<A HREF=\"%s\">.</A>\n", d, a_url);
475 if (!strcmp(p,
"hidden"))
476 out.
appendf(
"<LI type=\"circle\">%s (hidden)<A HREF=\"%s\">.</A>\n", d, a_url);
479 if (!strcmp(p,
"protected") && !req->
passwd)
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);
484 if (!strcmp(p,
"protected"))
485 out.
appendf(
"<LI type=\"square\"><A HREF=\"%s\"><font color=\"#FF0000\">%s</font></A>\n",
490 out.
appendf(
"<LI type=\"disk\"><A HREF=\"%s\">%s</A>\n", a_url, d);
500 static const char *ttags[] = {
"td",
"th"};
502 static int table_line_num = 0;
503 static int next_is_header = 0;
510 if (!strchr(buf,
'\t') || *buf ==
'\t') {
513 out.
append(
"</table>\n<pre>", 14);
520 if (!table_line_num) {
521 out.
append(
"</pre><table cellpadding=\"2\" cellspacing=\"1\">\n", 46);
526 is_header = (!table_line_num || next_is_header) && !strchr(buf,
':') && !
is_number(buf);
528 ttag = ttags[is_header];
536 if ((p = strchr(x,
'\n')))
539 while (x && strlen(x)) {
541 const char *cell =
xstrtok(&x,
'\t');
543 while (x && *x ==
'\t') {
548 out.
appendf(
"<%s colspan=\"%d\" align=\"%s\">%s</%s>",
550 is_header ?
"center" :
is_number(cell) ?
"right" :
"left",
557 next_is_header = is_header && strstr(buf,
"\t\t");
564 static char html[2 * 1024];
567 const char *
action, *description;
570 if ((p = strchr(x,
'\n')))
577 description =
xstrtok(&x,
'\t');
580 snprintf(html,
sizeof(html),
" <a href=\"%s\">%s</a>",
menu_url(req,
action), description);
593 FILE *fp = fopen(tmpfile,
"w+");
596 FILE *fp = fdopen(s,
"r");
600 isStatusLine, isHeaders, isActions, isBodyStart, isBody, isForward, isEof, isForwardEof, isSuccess, isError
601 } istate = isStatusLine;
604 const char *statusStr =
nullptr;
607 if (0 == strlen(req->
action))
609 else if (0 == strcasecmp(req->
action,
"menu"))
627 while ((reply=recv(s, buf,
sizeof(buf), 0)) > 0)
628 fwrite(buf, 1, reply, fp);
638 while (istate < isEof) {
639 if (!fgets(buf,
sizeof(buf), fp))
640 istate = istate == isForward ? isForwardEof : isEof;
649 istate = status == 200 ? isHeaders : isForward;
652 if (status == 401 || status == 407) {
659 printf(
"Status: %d %s", status, statusStr);
665 if (!strcmp(buf,
"\r\n")) {
666 fputs(
"Content-Type: text/html\r\n", stdout);
667 istate = isBodyStart;
670 if (strncasecmp(buf,
"Content-Type:", 13))
676 printf(
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
678 printf(
"<HTML><HEAD><TITLE>CacheMgr@%s: %s</TITLE>\n",
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");
683 printf(
"</HEAD><BODY>\n");
686 printf(
"<H2><a href=\"%s\">Cache Manager</a> menu for %s:</H2>",
690 printf(
"<P><A HREF=\"%s\">%s</A>\n<HR noshade size=\"1px\">\n",
691 menu_url(req,
"menu"),
"Cache Manager menu");
699 if (strncmp(buf,
"action:", 7) == 0) {
707 printf(
"<HR noshade size=\"1px\">\n");
724 fputs(out.
buf, stdout);
735 if (!strncasecmp(buf,
"WWW-Authenticate:", 17) || !strncasecmp(buf,
"Proxy-Authenticate:", 19));
746 printf(
"</table></PRE>\n");
761 printf(
"%s: internal bug: invalid state reached: %d",
script_name, istate);
789 static char buf[2 * 1024];
791 if (req ==
nullptr) {
800 if (req->
port == 0) {
804 if (req->
action ==
nullptr) {
808 if (strcmp(req->
action,
"authenticate") == 0) {
814 snprintf(buf,
sizeof(buf),
"target %s:%d not allowed in cachemgr.conf\n", req->
hostname, req->
port);
827 snprintf(buf,
sizeof(buf),
"Unknown Host: %s\n", req->
hostname);
831 snprintf(buf,
sizeof(buf),
"%s\n",
"Invalid Hostname");
842 if ((s = socket( AI->
ai_family, SOCK_STREAM, 0)) < 0) {
844 if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
847 snprintf(buf,
sizeof(buf),
"socket: %s\n",
xstrerr(xerrno));
867 l = snprintf(buf,
sizeof(buf),
868 "GET /squid-internal-mgr/%s%s%s HTTP/1.0\r\n"
870 "User-Agent: cachemgr.cgi/%s\r\n"
880 if (write(s, buf, l) < 0) {
881 fprintf(stderr,
"ERROR: (%d) writing request: '%s'\n", errno, buf);
883 debug(
"wrote request: '%s'\n", buf);
899 _setmode( _fileno( stdin ), _O_BINARY );
900 _setmode( _fileno( stdout ), _O_BINARY );
903 if ((s = strrchr(argv[0],
'\\')))
906 if ((s = strrchr(argv[0],
'/')))
913 if ((s = getenv(
"SCRIPT_NAME")) !=
nullptr)
917 while (argc > 1 && args[1][0] ==
'-') {
918 char option = args[1][1];
940 if ((s = getenv(
"REQUEST_METHOD")) ==
nullptr)
943 if (0 != strcasecmp(s,
"POST"))
946 if ((s = getenv(
"CONTENT_LENGTH")) ==
nullptr)
954 char *endptr = s+ strlen(s);
955 if ((len =
strtoll(s, &endptr, 10)) <= 0)
960 size_t bufLen = (len < 4096 ? len : 4095);
961 char *buf = (
char *)
xmalloc(bufLen + 1);
963 size_t readLen = fread(buf, 1, bufLen, stdin);
972 while (len > 0 && readLen) {
974 readLen = fread(temp, 1, 65535, stdin);
986 if ((s = getenv(
"QUERY_STRING")) ==
nullptr)
1011 if (strlen(buf) == 0 || strlen(buf) == 4000)
1014 if (strlen(buf) == 0)
1023 for (s = strtok(buf,
"&"); s !=
nullptr; s = strtok(
nullptr,
"&")) {
1027 if ((q = strchr(t,
'=')) ==
nullptr)
1037 if (0 == strcmp(t,
"server") && strlen(q))
1039 else if (0 == strcmp(t,
"host") && strlen(q))
1041 else if (0 == strcmp(t,
"port") && strlen(q))
1042 req->
port = atoi(q);
1043 else if (0 == strcmp(t,
"user_name") && strlen(q))
1045 else if (0 == strcmp(t,
"passwd") && strlen(q))
1047 else if (0 == strcmp(t,
"auth") && strlen(q))
1049 else if (0 == strcmp(t,
"operation"))
1051 else if (0 == strcmp(t,
"workers") && strlen(q))
1053 else if (0 == strcmp(t,
"processes") && strlen(q))
1062 if ((p = strtok(
nullptr,
":")))
1063 req->
port = atoi(p);
1067 debug(
"cmgr: got req: host: '%s' port: %d uname: '%s' passwd: '%s' auth: '%s' oper: '%s' workers: '%s' processes: '%s'\n",
1081 static char buf[1024];
1083 debug(
"cmgr: encoding for pub...\n");
1092 const int bufLen = snprintf(buf,
sizeof(buf),
"%s|%d|%s|%s",
1097 debug(
"cmgr: pre-encoded for pub: %s\n", buf);
1115 const char *host_name;
1116 const char *time_str;
1127 size_t decodedLen = 0;
1130 debug(
"cmgr: base64 decode failure. Incomplete auth token string.\n");
1134 buf[decodedLen] =
'\0';
1136 debug(
"cmgr: length ok\n");
1139 if ((host_name = strtok(buf,
"|")) ==
nullptr) {
1144 debug(
"cmgr: decoded host: '%s'\n", host_name);
1146 if ((time_str = strtok(
nullptr,
"|")) ==
nullptr) {
1151 debug(
"cmgr: decoded time: '%s' (now: %d)\n", time_str, (
int)
now);
1154 if ((user_name = strtok(
nullptr,
"|")) ==
nullptr) {
1160 debug(
"cmgr: decoded uname: '%s'\n", user_name);
1163 if ((passwd = strtok(
nullptr,
"|")) ==
nullptr) {
1169 debug(
"cmgr: decoded passwd: '%s'\n", passwd);
1177 if (strcasecmp(host_name, req->
hostname)) {
1182 debug(
"cmgr: verified auth. info.\n");
1204 static char buf[1024];
1205 size_t stringLength = 0;
1210 int bufLen = snprintf(buf,
sizeof(buf),
"%s:%s",
1215 if (encodedLen <= 0)
1218 char *str64 =
static_cast<char *
>(
xmalloc(encodedLen));
1225 stringLength += snprintf(buf,
sizeof(buf),
"Authorization: Basic %.*s\r\n", (
int)blen, str64);
1227 assert(stringLength <
sizeof(buf));
1229 snprintf(&buf[stringLength],
sizeof(buf) - stringLength,
"Proxy-Authorization: Basic %.*s\r\n", (
int)blen, str64);
1238 char config_line[
BUFSIZ];
1241 fp = fopen(
"cachemgr.conf",
"r");
1246 if (fp ==
nullptr) {
1247#ifdef CACHEMGR_HOSTNAME_DEFINED
1254 if (strcmp(hostname,
"localhost") == 0)
1265 while (fgets(config_line,
BUFSIZ, fp)) {
1266 char *token =
nullptr;
1267 strtok(config_line,
" \r\n\t");
1269 if (config_line[0] ==
'#')
1272 if (config_line[0] ==
'\0')
1275 if ((token = strtok(config_line,
":")) ==
nullptr)
1280 if (fnmatch(token, hostname, 0) != 0)
1285 if (strcmp(token, hostname) != 0)
1290 if ((token = strtok(
nullptr,
":")) !=
nullptr) {
1293 if (strcmp(token,
"*") == 0)
1296 else if (strcmp(token,
"any") == 0)
1299 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 *)
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
const char * FormatRfc1123(time_t)
struct hostent * gethostbyname()
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)
struct sockaddr * ai_addr
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)