mirror of
https://github.com/3proxy/3proxy.git
synced 2025-02-24 02:55:40 +08:00
579 lines
15 KiB
C
579 lines
15 KiB
C
/*
|
|
3APA3A simpliest proxy server
|
|
(c) 2002-2008 by ZARAZA <3APA3A@security.nnov.ru>
|
|
|
|
please read License Agreement
|
|
|
|
*/
|
|
|
|
#include "proxy.h"
|
|
|
|
#define RETURN(xxx) { param->res = xxx; goto CLEANRET; }
|
|
|
|
#define LINESIZE 2048
|
|
|
|
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, "&", 5);
|
|
break;
|
|
case '<':
|
|
if(delay){
|
|
stdpr((struct printparam*)cb, buf+i-delay, delay);
|
|
delay = 0;
|
|
}
|
|
stdpr((struct printparam*)cb, "<", 4);
|
|
break;
|
|
case '>':
|
|
if(delay){
|
|
stdpr((struct printparam*)cb, buf+i-delay, delay);
|
|
delay = 0;
|
|
}
|
|
stdpr((struct printparam*)cb, ">", 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> "
|
|
" </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\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;
|
|
int contentlen = 0;
|
|
int isform = 0;
|
|
|
|
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++;
|
|
contentlen = atoi(sb);
|
|
}
|
|
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(param->srv->singlepacket || 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 && (param->srv->singlepacket || 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:" ",
|
|
(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</td>"
|
|
"<td>%s</td>",
|
|
cp->traflim64 / (1024 * 1024),
|
|
rotations[cp->type],
|
|
cp->traf64,
|
|
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\"><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':
|
|
{
|
|
int l=0;
|
|
int error = 0;
|
|
|
|
if(!writable || fseek(writable, 0, 0)){
|
|
error = 1;
|
|
}
|
|
while((i = sockgetlinebuf(param, CLIENT, (unsigned char *)buf, LINESIZE - 1, '+', conf.timeouts[STRING_S])) > 0){
|
|
if(i > (contentlen - l)) i = (contentlen - l);
|
|
buf[i] = 0;
|
|
if(!l){
|
|
if(strncasecmp(buf, "conffile=", 9)) error = 1;
|
|
}
|
|
if(!error){
|
|
decodeurl((unsigned char *)buf, 1);
|
|
fprintf(writable, "%s", l? buf : buf + 9);
|
|
}
|
|
l += i;
|
|
if(l >= contentlen) break;
|
|
}
|
|
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);
|
|
(*param->srv->logfunc)(param, (unsigned char *)req);
|
|
if(req)myfree(req);
|
|
freeparam(param);
|
|
return (NULL);
|
|
}
|