3proxy/src/webadmin.c
z3APA3A eb829b062b Major code refactoring
- sockmapping rewritten from stratch to minimilse polling. poll() is now
only called if blocking is actually expected, splice pipes are now
polled if splice fails, buffers flushing is much more accurate.
- logging code moved to separate files
- signal masks added to client threads to prevent unneeded interruptions
- bandwidth limitation will not delay the thread after client or server
shutdown
2020-10-09 15:42:34 +03:00

584 lines
15 KiB
C

/*
3APA3A simpliest proxy server
(c) 2002-2016 by Vladimir Dubrovin <3proxy@3proxy.ru>
please read License Agreement
*/
#include "proxy.h"
#define RETURN(xxx) { param->res = xxx; goto CLEANRET; }
#define LINESIZE 65536
extern FILE *writable;
FILE * confopen();
extern void decodeurl(unsigned char *s, int filter);
struct printparam {
char buf[1024];
int inbuf;
struct clientparam *cp;
};
static void stdpr(struct printparam* pp, char *buf, int inbuf){
if((pp->inbuf + inbuf > 1024) || !buf) {
socksend(pp->cp->clisock, (unsigned char *)pp->buf, pp->inbuf, conf.timeouts[STRING_S]);
pp->inbuf = 0;
if(!buf) return;
}
if(inbuf >= 1000){
socksend(pp->cp->clisock, (unsigned char *)buf, inbuf, conf.timeouts[STRING_S]);
}
else {
memcpy(pp->buf + pp->inbuf, buf, inbuf);
pp->inbuf += inbuf;
}
}
static void stdcbf(void *cb, char *buf, int inbuf){
int delay = 0;
int i;
for(i = 0; i < inbuf; i++){
switch(buf[i]){
case '&':
if(delay){
stdpr((struct printparam*)cb, buf+i-delay, delay);
delay = 0;
}
stdpr((struct printparam*)cb, "&amp;", 5);
break;
case '<':
if(delay){
stdpr((struct printparam*)cb, buf+i-delay, delay);
delay = 0;
}
stdpr((struct printparam*)cb, "&lt;", 4);
break;
case '>':
if(delay){
stdpr((struct printparam*)cb, buf+i-delay, delay);
delay = 0;
}
stdpr((struct printparam*)cb, "&gt;", 4);
break;
default:
delay++;
break;
}
}
if(delay){
stdpr((struct printparam*)cb, buf+i-delay, delay);
}
}
/*
static char * templateprint(struct printparam* pp, int *level, struct dictionary *dict, char * template){
char *s, *s2;
for(; template && *template; ){
if(!( s = strchr(template, '<'))){
stdpr(pp, template, (int)strlen(template));
return template + strlen(template);
}
if(s[1] != '%' || s[2] == '%'){
stdpr(pp, template, (int)(s - template) + 1);
template = s + 1;
continue;
}
if(s[2] == '/' && (s2 = strchr(s + 2, '>')) && *(s2 - 1) == '%'){
if(--*level < 0) return NULL;
return s2 + 1;
}
}
return template;
}
*/
static void printstr(struct printparam* pp, char* str){
stdpr(pp, str, str?(int)strlen(str):0);
}
static void printval(void *value, int type, int level, struct printparam* pp){
struct node pn, cn;
struct property *p;
int i;
pn.iteration = NULL;
pn.parent = NULL;
pn.type = type;
pn.value = value;
printstr(pp, "<item>");
for(p = datatypes[type].properties; p; ) {
cn.iteration = NULL;
cn.parent = &pn;
cn.type = p->type;
cn.value = (*p->e_f)(&pn);
if(cn.value){
for(i = 0; i < level; i++) printstr(pp, "\t");
if(strcmp(p->name, "next")){
printstr(pp, "<parameter>");
printstr(pp, "<name>");
printstr(pp, p->name);
printstr(pp, "</name>");
printstr(pp, "<type>");
printstr(pp, datatypes[p->type].type);
printstr(pp, "</type>");
printstr(pp, "<description>");
printstr(pp, p->description);
printstr(pp, "</description>");
}
if(datatypes[p->type].p_f){
printstr(pp, "<value><![CDATA[");
(*datatypes[p->type].p_f)(&cn, stdcbf, pp);
printstr(pp, "]]></value>\n");
printstr(pp, "</parameter>");
}
else {
if(!strcmp(p->name, "next")){
/* printstr(pp, "<!-- -------------------- -->\n"); */
printstr(pp, "</item>\n<item>");
p = datatypes[type].properties;
pn.value = value = cn.value;
continue;
}
else {
printstr(pp, "\n");
printval(cn.value, cn.type, level+1, pp);
printstr(pp, "</parameter>");
}
}
}
p=p->next;
}
printstr(pp, "</item>");
}
char * admin_stringtable[]={
"HTTP/1.0 401 Authentication Required\r\n"
"WWW-Authenticate: Basic realm=\"proxy\"\r\n"
"Connection: close\r\n"
"Content-type: text/html; charset=us-ascii\r\n"
"\r\n"
"<html><head><title>401 Authentication Required</title></head>\r\n"
"<body><h2>401 Authentication Required</h2><h3>Access to requested resource disallowed by administrator or you need valid username/password to use this resource</h3></body></html>\r\n",
"HTTP/1.0 200 OK\r\n"
"Connection: close\r\n"
"Expires: Thu, 01 Dec 1994 16:00:00 GMT\r\n"
"Cache-Control: no-cache\r\n"
"Content-type: text/html\r\n"
"\r\n"
"<http><head><title>%s configuration page</title></head>\r\n"
"<table width=\'100%%\' border=\'0\'>\r\n"
"<tr><td width=\'150\' valign=\'top\'>\r\n"
"<h2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</h2>\r\n"
"<A HREF=\'/C'>Counters</A><br>\r\n"
"<A HREF=\'/R'>Reload</A><br>\r\n"
"<A HREF=\'/S'>Running Services</A><br>\r\n"
"<A HREF=\'/F'>Config</A>\r\n"
"</td><td>"
"<h2>%s %s configuration</h2>",
"HTTP/1.0 200 OK\r\n"
"Connection: close\r\n"
"Cache-Control: no-cache\r\n"
"Content-type: text/xml; charset=utf-8\r\n"
"Content-disposition: inline; filename=running.xml\r\n"
"\r\n"
"<?xml version=\"1.0\"?>\r\n"
"<?xml-stylesheet href=\"/SX\" type=\"text/css\"?>\r\n"
"<services>\r\n"
"<description>Services currently running and connected clients</description>\r\n",
"</services>\r\n",
"HTTP/1.0 200 OK\r\n"
"Connection: close\r\n"
"Cache-Control: no-cache\r\n"
"Content-type: text/css\r\n"
"\r\n"
"services {\r\n"
" display: block;\r\n"
" margin: 10px auto 10px auto;\r\n"
" width: 80%;\r\n"
" background: black;\r\n"
" font-family: sans-serif;\r\n"
" font-size: small;\r\n"
" color: silver;\r\n"
" }\r\n"
"item {\r\n"
" display: block;\r\n"
" margin-bottom: 10px;\r\n"
" border: 2px solid #CCC;\r\n"
" padding: 10px;\r\n"
" spacing: 2px;\r\n"
" }\r\n"
"parameter {\r\n"
" display: block;\r\n"
" padding: 2px;\r\n"
" margin-top: 10px;\r\n"
" border: 1px solid grey;\r\n"
" background: #EEE;\r\n"
" color: black;\r\n"
" }\r\n"
"name {\r\n"
" display: inline;\r\n"
" float: left;\r\n"
" margin-right: 5px;\r\n"
" font-weight: bold;\r\n"
" }\r\n"
"type {\r\n"
" display: inline;\r\n"
" font-size: x-small;\r\n"
" margin-right: 5px;\r\n"
" color: #666;\r\n"
" white-space: nowrap;\r\n"
" font-style: italic;\r\n"
" }\r\n"
"description {\r\n"
" display: inline;\r\n"
" margin-right: 5px;\r\n"
" white-space: nowrap;\r\n"
" }\r\n"
"value {\r\n"
" display: block;\r\n"
" margin-right: 5px;\r\n"
" }\r\n",
"<br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br />\r\n"
"<pre><font size=\'-2\'><b>"
COPYRIGHT
"</b></font>\r\n"
"</td></tr></table></body></html>",
"<h3>Counters</h3>\r\n"
"<table border = \'1\'>\r\n"
"<tr align=\'center\'><td>Description</td><td>Active</td>"
"<td>Users</td><td>Source Address</td><td>Destination Address</td>"
"<td>Port</td>"
"<td>Limit</td><td>Units</td><td>Value</td>"
"<td>Reset</td><td>Updated</td><td>Num</td></tr>\r\n",
"</table>\r\n",
NULL
};
#define authreq admin_stringtable[0]
#define ok admin_stringtable[1]
#define xml admin_stringtable[2]
#define postxml admin_stringtable[3]
#define style admin_stringtable[4]
#define tail admin_stringtable[5]
#define counters admin_stringtable[6]
#define counterstail admin_stringtable[7]
static int printportlist(char *buf, int bufsize, struct portlist* pl, char * delim){
int printed = 0;
for(; pl; pl = pl->next){
if(printed > (bufsize - 64)) break;
if(pl->startport != pl->endport)
printed += sprintf(buf+printed, "%hu-%hu%s", pl->startport, pl->endport, pl->next?delim:"");
else {
/*
struct servent *se=NULL;
if(pl->startport)se = getservbyport((int)ntohs(pl->startport), NULL);
printed += sprintf(buf+printed, "%hu(%s)%s", pl->startport, se?se->s_name:"unknown", pl->next?delim:"");
*/
printed += sprintf(buf+printed, "%hu%s", pl->startport, pl->next?delim:"");
}
if(printed > (bufsize - 64)) {
printed += sprintf(buf+printed, "...");
break;
}
}
return printed;
}
static int printuserlist(char *buf, int bufsize, struct userlist* ul, char * delim){
int printed = 0;
for(; ul; ul = ul->next){
if(printed > (bufsize - 64)) break;
printed += sprintf(buf+printed, "%s%s", ul->user, ul->next?delim:"");
if(printed > (bufsize - 64)) {
printed += sprintf(buf+printed, "...");
break;
}
}
return printed;
}
int printiple(char *buf, struct iplist* ipl);
static int printiplist(char *buf, int bufsize, struct iplist* ipl, char * delim){
int printed = 0;
for(; ipl; ipl = ipl->next){
if(printed > (bufsize - 128)) break;
printed += printiple(buf+printed, ipl);
if(printed > (bufsize - 128)) {
printed += sprintf(buf+printed, "...");
break;
}
}
return printed;
}
void * adminchild(struct clientparam* param) {
int i, res;
char * buf;
char username[256];
char *sb;
char *req = NULL;
struct printparam pp;
unsigned contentlen = 0;
int isform = 0;
int limited = 0;
limited =param->srv->singlepacket;
pp.inbuf = 0;
pp.cp = param;
buf = myalloc(LINESIZE);
if(!buf) {RETURN(555);}
i = sockgetlinebuf(param, CLIENT, (unsigned char *)buf, LINESIZE - 1, '\n', conf.timeouts[STRING_S]);
if(i<5 || ((buf[0]!='G' || buf[1]!='E' || buf[2]!='T' || buf[3]!=' ' || buf[4]!='/') &&
(buf[0]!='P' || buf[1]!='O' || buf[2]!='S' || buf[3]!='T' || buf[4]!=' ' || buf[5]!='/')))
{
RETURN(701);
}
buf[i] = 0;
sb = strchr(buf+5, ' ');
if(!sb){
RETURN(702);
}
*sb = 0;
req = mystrdup(buf + ((*buf == 'P')? 6 : 5));
while((i = sockgetlinebuf(param, CLIENT, (unsigned char *)buf, LINESIZE - 1, '\n', conf.timeouts[STRING_S])) > 2){
buf[i] = 0;
if(i > 19 && (!strncasecmp(buf, "authorization", 13))){
sb = strchr(buf, ':');
if(!sb)continue;
++sb;
while(isspace(*sb))sb++;
if(!*sb || strncasecmp(sb, "basic", 5)){
continue;
}
sb+=5;
while(isspace(*sb))sb++;
i = de64((unsigned char *)sb, (unsigned char *)username, 255);
if(i<=0)continue;
username[i] = 0;
sb = strchr((char *)username, ':');
if(sb){
*sb = 0;
if(param->password)myfree(param->password);
param->password = (unsigned char *)mystrdup(sb+1);
}
if(param->username) myfree(param->username);
param->username = (unsigned char *)mystrdup(username);
continue;
}
else if(i > 15 && (!strncasecmp(buf, "content-length:", 15))){
sb = buf + 15;
while(isspace(*sb))sb++;
sscanf(sb, "%u", &contentlen);
if(contentlen > LINESIZE*1024) contentlen = 0;
}
else if(i > 13 && (!strncasecmp(buf, "content-type:", 13))){
sb = buf + 13;
while(isspace(*sb))sb++;
if(!strncasecmp(sb, "x-www-form-urlencoded", 21)) isform = 1;
}
}
param->operation = ADMIN;
if(isform && contentlen) {
printstr(&pp, "HTTP/1.0 100 Continue\r\n\r\n");
stdpr(&pp, NULL, 0);
}
res = (*param->srv->authfunc)(param);
if(res && res != 10) {
printstr(&pp, authreq);
RETURN(res);
}
if(limited || param->redirected){
if(*req == 'C') req[1] = 0;
else *req = 0;
}
sprintf(buf, ok, conf.stringtable?(char *)conf.stringtable[2]:"3proxy", conf.stringtable?(char *)conf.stringtable[2]:"3[APA3A] tiny proxy", conf.stringtable?(char *)conf.stringtable[3]:"");
if(*req != 'S') printstr(&pp, buf);
switch(*req){
case 'C':
printstr(&pp, counters);
{
struct trafcount *cp;
int num = 0;
for(cp = conf.trafcounter; cp; cp = cp->next, num++){
int inbuf = 0;
if(cp->ace && (limited || param->redirected)){
if(!ACLmatches(cp->ace, param))continue;
}
if(req[1] == 'S' && atoi(req+2) == num) cp->disabled=0;
if(req[1] == 'D' && atoi(req+2) == num) cp->disabled=1;
inbuf += sprintf(buf, "<tr>"
"<td>%s</td><td><A HREF=\'/C%c%d\'>%s</A></td><td>",
(cp->comment)?cp->comment:"&nbsp;",
(cp->disabled)?'S':'D',
num,
(cp->disabled)?"NO":"YES"
);
if(!cp->ace || !cp->ace->users){
inbuf += sprintf(buf+inbuf, "<center>ANY</center>");
}
else {
inbuf += printuserlist(buf+inbuf, LINESIZE-800, cp->ace->users, ",<br />\r\n");
}
inbuf += sprintf(buf+inbuf, "</td><td>");
if(!cp->ace || !cp->ace->src){
inbuf += sprintf(buf+inbuf, "<center>ANY</center>");
}
else {
inbuf += printiplist(buf+inbuf, LINESIZE-512, cp->ace->src, ",<br />\r\n");
}
inbuf += sprintf(buf+inbuf, "</td><td>");
if(!cp->ace || !cp->ace->dst){
inbuf += sprintf(buf+inbuf, "<center>ANY</center>");
}
else {
inbuf += printiplist(buf+inbuf, LINESIZE-512, cp->ace->dst, ",<br />\r\n");
}
inbuf += sprintf(buf+inbuf, "</td><td>");
if(!cp->ace || !cp->ace->ports){
inbuf += sprintf(buf+inbuf, "<center>ANY</center>");
}
else {
inbuf += printportlist(buf+inbuf, LINESIZE-128, cp->ace->ports, ",<br />\r\n");
}
if(cp->type == NONE) {
inbuf += sprintf(buf+inbuf,
"</td><td colspan=\'6\' align=\'center\'>exclude from limitation</td></tr>\r\n"
);
}
else {
inbuf += sprintf(buf+inbuf,
"</td><td>%"PRINTF_INT64_MODIFIER"u</td>"
"<td>MB%s</td>"
"<td>%"PRINTF_INT64_MODIFIER"u.%"PRINTF_INT64_MODIFIER"u</td>"
"<td>%s</td>",
cp->traflim64 / (1024 * 1024),
rotations[cp->type],
cp->traf64 / (1024 * 1024),
(((cp->traf64/16) *10) / (64*1024))%10,
cp->cleared?ctime(&cp->cleared):"never"
);
inbuf += sprintf(buf + inbuf,
"<td>%s</td>"
"<td>%i</td>"
"</tr>\r\n",
cp->updated?ctime(&cp->updated):"never",
cp->number
);
}
printstr(&pp, buf);
}
}
printstr(&pp, counterstail);
break;
case 'R':
conf.needreload = 1;
printstr(&pp, "<h3>Reload scheduled</h3>");
break;
case 'S':
{
if(req[1] == 'X'){
printstr(&pp, style);
break;
}
printstr(&pp, xml);
printval(conf.services, TYPE_SERVER, 0, &pp);
printstr(&pp, postxml);
}
break;
case 'F':
{
FILE *fp;
char buf[256];
fp = confopen();
if(!fp){
printstr(&pp, "<h3><font color=\"red\">Failed to open config file</font></h3>");
break;
}
printstr(&pp, "<h3>Please be careful editing config file remotely</h3>");
printstr(&pp, "<form method=\"POST\" action=\"/U\" enctype=\"application/x-www-form-urlencoded\"><textarea cols=\"80\" rows=\"30\" name=\"conffile\">");
while(fgets(buf, 256, fp)){
printstr(&pp, buf);
}
if(!writable) fclose(fp);
printstr(&pp, "</textarea><br><input type=\"Submit\"></form>");
break;
}
case 'U':
{
unsigned l=0;
int error = 0;
if(!writable || !contentlen || fseek(writable, 0, 0)){
error = 1;
}
while(l < contentlen && (i = sockgetlinebuf(param, CLIENT, (unsigned char *)buf, (contentlen - l) > LINESIZE - 1?LINESIZE - 1:contentlen - l, '+', conf.timeouts[STRING_S])) > 0){
if(i > (contentlen - l)) i = (contentlen - l);
if(!l){
if(i<9 || strncasecmp(buf, "conffile=", 9)) error = 1;
}
if(!error){
buf[i] = 0;
decodeurl((unsigned char *)buf, 1);
fprintf(writable, "%s", l? buf : buf + 9);
}
l += i;
}
if(writable && !error){
fflush(writable);
#ifndef _WINCE
ftruncate(fileno(writable), ftell(writable));
#endif
}
printstr(&pp, error? "<h3><font color=\"red\">Config file is not writable</font></h3>Make sure you have \"writable\" command in configuration file":
"<h3>Configuration updated</h3>");
}
break;
default:
printstr(&pp, (char *)conf.stringtable[WEBBANNERS]);
break;
}
if(*req != 'S') printstr(&pp, tail);
CLEANRET:
printstr(&pp, NULL);
if(buf) myfree(buf);
dolog(param, (unsigned char *)req);
if(req)myfree(req);
freeparam(param);
return (NULL);
}