mirror of
https://github.com/3proxy/3proxy.git
synced 2026-10-17 08:55:49 +08:00
A reload reads the file into a new configuration, allocated from the defaults, and prepares its services without starting them. Only if the whole file is read without an error does it come into force: the old configuration's services stop accepting, the new ones start. On an error everything prepared is dropped and the old configuration, which never stopped, stays in force. Each service holds its configuration until it is freed, after its last client; a configuration no longer in force is released when the last holder lets go (confhold/confput). freeconf() now only releases what a configuration holds, touching nothing global. What runs without a service of its own reads the configuration in force, runconf, rather than conf, which a reload points at the one being read: the resolver, which holds it while it waits (a client resolves with its own service's), the log, the thread pool, the admin pages, which walk the services under config_mutex, and the signal handlers. The users table, havelog and the resolver choice are per configuration now, the resolver applied when the configuration comes into force, so a refused file leaves the users as they were. The log file, the configuration file name, the string table, daemon mode and the filter list belong to the process and are handed from one configuration to the next. Settings left out of a new file go back to their defaults, where before they kept the old values. The gap between the old and the new listening sockets remains: a connection queued on an old service's socket when it closes is reset. Handing the socket over is the next phase. A test case reloads a broken file (nothing changes, alice still gets in, bob from the refused file does not) and a good one (bob only, the service on its new port, the old one closed, a download through the old service finishing whole). 40 reloads, a third refused, under load show no memory errors under AddressSanitizer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2654 lines
71 KiB
C
2654 lines
71 KiB
C
/*
|
|
3APA3A simplest proxy server
|
|
(c) 2002-2026 by Vladimir Dubrovin <vlad@3proxy.org>
|
|
|
|
please read License Agreement
|
|
|
|
*/
|
|
|
|
#include "proxy.h"
|
|
|
|
#ifdef WITH_HTTPSRV
|
|
static int addhttprule(char *op, char *host, char *url, char *params);
|
|
#endif
|
|
#include "mdhash.h"
|
|
#ifdef WITH_SSL
|
|
void ssl_install(void);
|
|
#endif
|
|
#ifdef WITH_PCRE
|
|
void pcre_install(void);
|
|
#endif
|
|
#ifdef WITH_TRANSPARENT
|
|
void transparent_install(void);
|
|
#endif
|
|
#ifndef _WIN32
|
|
#include <sys/resource.h>
|
|
#include <pwd.h>
|
|
#include <grp.h>
|
|
#ifndef NOPLUGINS
|
|
#include <dlfcn.h>
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef DEFAULTCONFIG
|
|
#define DEFAULTCONFIG conf->stringtable[25]
|
|
#endif
|
|
|
|
_3proxy_mutex_t bandlim_mutex;
|
|
_3proxy_mutex_t connlim_mutex;
|
|
_3proxy_mutex_t tc_mutex;
|
|
_3proxy_mutex_t config_mutex;
|
|
int runningservices = 0;
|
|
_3proxy_mutex_t runningservices_mutex;
|
|
_3proxy_sem_t serviceexited;
|
|
|
|
int linenum = 0;
|
|
|
|
struct counter_header cheader = {"3CF", (time_t)0};
|
|
struct counter_record crecord;
|
|
|
|
/* The values of the numbered counters, process-wide: a counter of a number
|
|
met for the first time takes its value from the counter file. Only the
|
|
thread reading the configuration adds to the list. */
|
|
static struct trafcell {
|
|
struct trafcell *next;
|
|
unsigned number;
|
|
struct trafvalue v;
|
|
} *trafcells = NULL;
|
|
|
|
struct trafvalue * trafvalue(unsigned number){
|
|
struct trafcell *tc;
|
|
|
|
for(tc = trafcells; tc; tc = tc->next) if(tc->number == number) return &tc->v;
|
|
if(!(tc = malloc(sizeof(*tc)))) return NULL;
|
|
memset(tc, 0, sizeof(*tc));
|
|
tc->number = number;
|
|
if(conf->counterd >= 0) {
|
|
lseek(conf->counterd,
|
|
sizeof(struct counter_header) + (number - 1) * sizeof(struct counter_record),
|
|
SEEK_SET);
|
|
memset(&crecord, 0, sizeof(struct counter_record));
|
|
if(read(conf->counterd, &crecord, sizeof(struct counter_record)) == sizeof(struct counter_record)){
|
|
if(crecord.cleared < 0 || crecord.cleared >= MAX_COUNTER_TIME || crecord.updated < 0 || crecord.updated >= MAX_COUNTER_TIME){
|
|
free(tc);
|
|
return NULL;
|
|
}
|
|
tc->v.traf64 = crecord.traf64;
|
|
tc->v.cleared = crecord.cleared;
|
|
tc->v.updated = crecord.updated;
|
|
}
|
|
}
|
|
tc->next = trafcells;
|
|
trafcells = tc;
|
|
return &tc->v;
|
|
}
|
|
|
|
int mainprepare(int argc, char** argv, void **service);
|
|
int mainrun(void *service);
|
|
void maindrop(void *service);
|
|
|
|
struct proxydef childdef = {NULL, 0, 0, S_NOSERVICE, ""};
|
|
|
|
#define STRINGBUF 65535
|
|
#define NPARAMS 4096
|
|
|
|
#ifndef _WIN32
|
|
char *chrootp = NULL;
|
|
static pthread_attr_t pa;
|
|
#endif
|
|
char * curconf = NULL;
|
|
|
|
FILE * confopen(){
|
|
curconf = conf->conffile;
|
|
#ifndef _WIN32
|
|
if(chrootp){
|
|
if(strstr(curconf, chrootp) == curconf)
|
|
curconf += strlen(chrootp);
|
|
}
|
|
#endif
|
|
return fopen(curconf, "r");
|
|
}
|
|
|
|
|
|
#ifdef _WIN32
|
|
DWORD WINAPI startsrv(LPVOID data) {
|
|
#else
|
|
void * startsrv(void * data) {
|
|
#endif
|
|
/* counted by start_proxy_thread() before the thread is created */
|
|
mainrun(data);
|
|
_3proxy_mutex_lock(&runningservices_mutex);
|
|
runningservices--;
|
|
_3proxy_mutex_unlock(&runningservices_mutex);
|
|
_3proxy_sem_unlock(serviceexited);
|
|
return 0;
|
|
}
|
|
|
|
int included =0;
|
|
|
|
int getrotate(char c){
|
|
switch(c){
|
|
case 'c':
|
|
case 'C':
|
|
return MINUTELY;
|
|
case 'h':
|
|
case 'H':
|
|
return HOURLY;
|
|
case 'd':
|
|
case 'D':
|
|
return DAILY;
|
|
case 'w':
|
|
case 'W':
|
|
return WEEKLY;
|
|
case 'y':
|
|
case 'Y':
|
|
return ANNUALLY;
|
|
case 'm':
|
|
case 'M':
|
|
return MONTHLY;
|
|
default:
|
|
return NEVER;
|
|
}
|
|
}
|
|
|
|
|
|
unsigned char * dologname (unsigned char *buf, unsigned char *name, const unsigned char *ext, ROTATION lt, time_t t) {
|
|
struct tm *ts;
|
|
static const char * const rot_fmt[] = {
|
|
[NONE] = "%s",
|
|
[ANNUALLY] = "%s.%04d",
|
|
[MONTHLY] = "%s.%04d.%02d",
|
|
[DAILY] = "%s.%04d.%02d.%02d",
|
|
[HOURLY] = "%s.%04d.%02d.%02d-%02d",
|
|
[MINUTELY] = "%s.%04d.%02d.%02d-%02d.%02d",
|
|
};
|
|
|
|
ts = localtime(&t);
|
|
if(strlen((char *)name) >= 4096){
|
|
*buf = 0;
|
|
return buf;
|
|
}
|
|
if(strchr((char *)name, '%')){
|
|
dobuf2(NULL, buf, NULL, NULL, ts, (char *)name);
|
|
}
|
|
else if(lt == WEEKLY){
|
|
t = t - (ts->tm_wday * (60*60*24));
|
|
ts = localtime(&t);
|
|
sprintf((char *)buf, "%s.%04d.%02d.%02d", name, ts->tm_year+1900, ts->tm_mon+1, ts->tm_mday);
|
|
}
|
|
else if((unsigned)lt < sizeof(rot_fmt)/sizeof(rot_fmt[0]) && rot_fmt[lt]){
|
|
sprintf((char *)buf, rot_fmt[lt], name, ts->tm_year+1900, ts->tm_mon+1, ts->tm_mday, ts->tm_hour, ts->tm_min);
|
|
}
|
|
if(ext){
|
|
strcat((char *)buf, ".");
|
|
strcat((char *)buf, (char *)ext);
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
static int deferred_add(void *service);
|
|
static int startservice(void *service);
|
|
|
|
/* The service is set up here, in the thread reading the configuration, so
|
|
an error in its options stops the configuration; its own thread then
|
|
listens and serves. During a reload it is started only once the whole
|
|
file has been read. */
|
|
int start_proxy_thread(struct child * chp){
|
|
void *service;
|
|
int res;
|
|
|
|
if((res = mainprepare(chp->argc, (char **)chp->argv, &service))) {
|
|
fprintf(stderr, "Service not started on line: %d%s\n", linenum, res == 2? ": insufficient memory": res == 3? ": too many filters": res == 4? ": network namespace unavailable":"");
|
|
return(40);
|
|
}
|
|
switch(deferred_add(service)){
|
|
case 1:
|
|
return 0;
|
|
case -1:
|
|
maindrop(service);
|
|
fprintf(stderr, "Service not started on line: %d: insufficient memory\n", linenum);
|
|
return(40);
|
|
}
|
|
return startservice(service);
|
|
}
|
|
|
|
/* Starts the thread of a prepared service; 40 if it could not be. */
|
|
static int startservice(void *service){
|
|
pthread_t thread;
|
|
int res;
|
|
#ifdef _WIN32
|
|
HANDLE h;
|
|
#endif
|
|
|
|
_3proxy_mutex_lock(&runningservices_mutex);
|
|
runningservices++;
|
|
_3proxy_mutex_unlock(&runningservices_mutex);
|
|
#ifdef _WIN32
|
|
#ifndef _WINCE
|
|
h = (HANDLE)_beginthreadex((LPSECURITY_ATTRIBUTES )NULL, 16384+conf->stacksize, (void *)startsrv, service, (DWORD)0, &thread);
|
|
#else
|
|
h = (HANDLE)CreateThread((LPSECURITY_ATTRIBUTES )NULL, 16384+conf->stacksize, (void *)startsrv, service, (DWORD)0, &thread);
|
|
#endif
|
|
if(h)CloseHandle(h);
|
|
else {
|
|
#else
|
|
pthread_attr_init(&pa);
|
|
pthread_attr_setstacksize(&pa,threadstacksize(conf->stacksize));
|
|
pthread_attr_setdetachstate(&pa,PTHREAD_CREATE_DETACHED);
|
|
res = pthread_create(&thread, &pa, startsrv, service);
|
|
pthread_attr_destroy(&pa);
|
|
if(res){
|
|
#endif
|
|
_3proxy_mutex_lock(&runningservices_mutex);
|
|
runningservices--;
|
|
_3proxy_mutex_unlock(&runningservices_mutex);
|
|
maindrop(service);
|
|
fprintf(stderr, "Failed to create service thread on line %d, try to set larger stacksize\n", linenum);
|
|
return(40);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int h_proxy(int argc, unsigned char ** argv){
|
|
struct child ch;
|
|
|
|
ch.argc = argc;
|
|
ch.argv = argv;
|
|
if(!strcmp((char *)argv[0], "proxy")) {
|
|
childdef.pf = proxychild;
|
|
childdef.port = 3128;
|
|
childdef.isudp = 0;
|
|
childdef.service = S_PROXY;
|
|
#ifdef WITH_FTP
|
|
childdef.helpmessage = " -Xftp - fetch ftp:// URLs\n";
|
|
#endif
|
|
#ifdef NOIPV6
|
|
if(!conf->resolvfunc || (conf->resolvfunc == myresolver && !dns_table.poolsize)){
|
|
fprintf(stderr, "[line %d] Warning: no nserver/nscache configured, proxy may run very slow\n", linenum);
|
|
}
|
|
#endif
|
|
}
|
|
else if(!strcmp((char *)argv[0], "pop3p")) {
|
|
childdef.pf = pop3pchild;
|
|
childdef.port = 110;
|
|
childdef.isudp = 0;
|
|
childdef.service = S_POP3P;
|
|
childdef.helpmessage = " -hdefault_host[:port] - use this host and port as default if no host specified\n -x - disable STARTTLS\n";
|
|
}
|
|
else if(!strcmp((char *)argv[0], "imapp")) {
|
|
childdef.pf = imappchild;
|
|
childdef.port = 143;
|
|
childdef.isudp = 0;
|
|
childdef.service = S_IMAPP;
|
|
childdef.helpmessage = " -hdefault_host[:port] - use this host and port as default if no host specified\n -x - disable STARTTLS\n";
|
|
}
|
|
else if(!strcmp((char *)argv[0], "smtpp")) {
|
|
childdef.pf = smtppchild;
|
|
childdef.port = 587;
|
|
childdef.isudp = 0;
|
|
childdef.service = S_SMTPP;
|
|
childdef.helpmessage = " -hdefault_host[:port] - use this host and port as default if no host specified\n -x - disable STARTTLS\n";
|
|
}
|
|
else if(!strcmp((char *)argv[0], "ftppr")) {
|
|
childdef.pf = ftpprchild;
|
|
childdef.port = 21;
|
|
childdef.isudp = 0;
|
|
childdef.service = S_FTPPR;
|
|
childdef.helpmessage = " -hdefault_host[:port] - use this host and port as default if no host specified\n";
|
|
}
|
|
else if(!strcmp((char *)argv[0], "socks")) {
|
|
childdef.pf = sockschild;
|
|
childdef.port = 1080;
|
|
childdef.isudp = 0;
|
|
childdef.service = S_SOCKS;
|
|
#ifdef NOIPV6
|
|
if(!conf->resolvfunc || (conf->resolvfunc == myresolver && !dns_table.poolsize)){
|
|
fprintf(stderr, "[line %d] Warning: no nserver/nscache configured, socks may run very slow\n", linenum);
|
|
}
|
|
#endif
|
|
}
|
|
else if(!strcmp((char *)argv[0], "auto")) {
|
|
childdef.pf = autochild;
|
|
childdef.port = 8080;
|
|
childdef.isudp = 0;
|
|
childdef.service = S_AUTO;
|
|
childdef.helpmessage = "";
|
|
}
|
|
else if(!strcmp((char *)argv[0], "tcppm")) {
|
|
childdef.pf = tcppmchild;
|
|
childdef.port = 0;
|
|
childdef.isudp = 0;
|
|
childdef.service = S_TCPPM;
|
|
childdef.helpmessage = "";
|
|
}
|
|
else if(!strcmp((char *)argv[0], "tlspr")) {
|
|
childdef.pf = tlsprchild;
|
|
childdef.port = 1443;
|
|
childdef.isudp = 0;
|
|
childdef.service = S_TLSPR;
|
|
childdef.helpmessage = "";
|
|
}
|
|
else if(!strcmp((char *)argv[0], "udppm")) {
|
|
childdef.pf = udppmchild;
|
|
childdef.port = 0;
|
|
childdef.isudp = 1;
|
|
childdef.service = S_UDPPM;
|
|
childdef.helpmessage = " -s single packet UDP service for request/reply (DNS-like) services\n";
|
|
}
|
|
#ifdef WITH_HTTPSRV
|
|
else if(!strcmp((char *)argv[0], "admin")) {
|
|
/* The same service as httpsrv, with the administration pages
|
|
declared for it. */
|
|
if(addhttprule("admin_counters", "*", "/C*", NULL) ||
|
|
addhttprule("admin_reload", "*", "/R", NULL) ||
|
|
addhttprule("admin_services", "*", "/S*", NULL) ||
|
|
addhttprule("admin", "*", "**", NULL)){
|
|
fprintf(stderr, "Failed to declare the admin pages, line %d\n", linenum);
|
|
return 1;
|
|
}
|
|
childdef.pf = httpsrvchild;
|
|
childdef.port = 80;
|
|
childdef.isudp = 0;
|
|
childdef.service = S_HTTPSRV;
|
|
}
|
|
#endif
|
|
#ifdef WITH_HTTPSRV
|
|
else if(!strcmp((char *)argv[0], "httpsrv")) {
|
|
childdef.pf = httpsrvchild;
|
|
childdef.port = 80;
|
|
childdef.isudp = 0;
|
|
childdef.service = S_HTTPSRV;
|
|
childdef.helpmessage = " HTTP server, /echo describes the connection, /data?size=N returns N bytes\n";
|
|
}
|
|
#endif
|
|
else if(!strcmp((char *)argv[0], "dnspr")) {
|
|
childdef.pf = dnsprchild;
|
|
childdef.port = 53;
|
|
childdef.isudp = 1;
|
|
childdef.service = S_DNSPR;
|
|
childdef.helpmessage = " -s - simple DNS forwarding - do not use 3proxy resolver / name cache\n -Fip - fake: answer all A queries with this IP\n";
|
|
#ifndef NOIPV6
|
|
if(!conf->resolvfunc || (conf->resolvfunc == myresolver && !dns_table.poolsize) || conf->resolvfunc == fakeresolver){
|
|
fprintf(stderr, "[line %d] Warning: no nserver/nscache configured, dnspr will not work as expected\n", linenum);
|
|
}
|
|
#endif
|
|
}
|
|
return start_proxy_thread(&ch);
|
|
}
|
|
|
|
static int h_internal(int argc, unsigned char ** argv){
|
|
#ifdef WITH_UN
|
|
if(!strncmp((char *)argv[1], "unix:", 5)){
|
|
make_un(argv[1] +5, (struct sockaddr_un *)&conf->intsa);
|
|
}
|
|
else
|
|
#endif
|
|
getip46(46, argv[1], (struct sockaddr *)&conf->intsa);
|
|
return 0;
|
|
}
|
|
|
|
static int h_external(int argc, unsigned char ** argv){
|
|
int res;
|
|
#ifndef NOIPV6
|
|
PROXYSOCKADDRTYPE sa6;
|
|
memset(&sa6, 0, sizeof(sa6));
|
|
res = getip46(46, argv[1], (struct sockaddr *)&sa6);
|
|
if(!res) return 1;
|
|
if (*SAFAMILY(&sa6)==AF_INET) conf->extsa = sa6;
|
|
else conf->extsa6 = sa6;
|
|
#else
|
|
res = getip46(46, argv[1], (struct sockaddr *)&conf->extsa);
|
|
if(!res) return 1;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* ODBC drivers require noticeably more stack than the file logger, raise
|
|
the client thread stack size unless a larger one is configured
|
|
explicitly. An explicit stacksize placed after the log command still wins.
|
|
*/
|
|
#define LOGSTACKSIZE 32768
|
|
|
|
static int h_log(int argc, unsigned char ** argv){
|
|
unsigned char tmpbuf[8192];
|
|
int notchanged = 0;
|
|
|
|
|
|
conf->havelog = 1;
|
|
if(argc > 1 && conf->logtarget && !strcmp((char *)conf->logtarget, (char *)argv[1])) {
|
|
notchanged = 1;
|
|
}
|
|
if(!notchanged && conf->logtarget){
|
|
free(conf->logtarget);
|
|
conf->logtarget = NULL;
|
|
}
|
|
if(argc > 1) {
|
|
if(!strcmp((char *) argv[1], "/dev/null")) {
|
|
conf->logfunc = lognone;
|
|
return 0;
|
|
}
|
|
if(!notchanged) conf->logtarget = (unsigned char *)strdup((char *)argv[1]);
|
|
if(*argv[1]=='@'){
|
|
#ifndef _WIN32
|
|
conf->logfunc = logsyslog;
|
|
if(notchanged) return 0;
|
|
/* connected now: after a chroot there is no /dev/log to find */
|
|
openlog((char *)conf->logtarget+1, LOG_PID | LOG_NDELAY, LOG_DAEMON);
|
|
#endif
|
|
}
|
|
#ifdef WITH_ODBC
|
|
else if(*argv[1]=='&'){
|
|
conf->logfunc = logsql;
|
|
if(conf->stacksize < LOGSTACKSIZE) conf->stacksize = LOGSTACKSIZE;
|
|
if(notchanged) return 0;
|
|
_3proxy_mutex_lock(&log_mutex);
|
|
close_sql();
|
|
init_sql((char *)argv[1]+1);
|
|
_3proxy_mutex_unlock(&log_mutex);
|
|
}
|
|
#endif
|
|
#ifndef NORADIUS
|
|
else if(!strcmp((char *)argv[1],"radius")){
|
|
conf->logfunc = logradius;
|
|
}
|
|
#endif
|
|
else {
|
|
if(argc > 2) {
|
|
conf->logtype = getrotate(*argv[2]);
|
|
}
|
|
conf->logfunc = logstdout;
|
|
if(notchanged) return 0;
|
|
conf->logtime = time(0);
|
|
if(conf->logname)free(conf->logname);
|
|
conf->logname = (unsigned char *)strdup((char *)argv[1]);
|
|
if(conf->stdlog) conf->stdlog = freopen((char *)dologname (tmpbuf, conf->logname, NULL, conf->logtype, conf->logtime), "a", conf->stdlog);
|
|
else conf->stdlog = fopen((char *)dologname (tmpbuf, conf->logname, NULL, conf->logtype, conf->logtime), "a");
|
|
if(!conf->stdlog){
|
|
perror((char *)tmpbuf);
|
|
return 1;
|
|
}
|
|
|
|
}
|
|
}
|
|
else conf->logfunc = logstdout;
|
|
return 0;
|
|
}
|
|
|
|
static int h_stacksize(int argc, unsigned char **argv){
|
|
conf->stacksize = atoi((char *)argv[1]);
|
|
return 0;
|
|
}
|
|
|
|
static int h_threadpool(int argc, unsigned char **argv){
|
|
conf->poolmax = atoi((char *)argv[1]);
|
|
if(argc > 2) conf->pooltimeout = atoi((char *)argv[2]);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int h_force(int argc, unsigned char **argv){
|
|
conf->noforce = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int h_noforce(int argc, unsigned char **argv){
|
|
conf->noforce = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int h_service(int argc, unsigned char **argv){
|
|
return 0;
|
|
}
|
|
|
|
static int h_daemon(int argc, unsigned char **argv){
|
|
if(!conf->demon)daemonize();
|
|
conf->demon = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int h_config(int argc, unsigned char **argv){
|
|
if(conf->conffile)free(conf->conffile);
|
|
conf->conffile = strdup((char *)argv[1]);
|
|
if(!conf->conffile) return 21;
|
|
return 0;
|
|
}
|
|
|
|
static int h_include(int argc, unsigned char **argv){
|
|
int res;
|
|
FILE *fp1;
|
|
|
|
fp1 = fopen((char *)argv[1], "r");
|
|
if(!fp1){
|
|
fprintf(stderr, "Unable to open included file: %s\n", argv[1]);
|
|
return 1;
|
|
}
|
|
res = readconfig(fp1);
|
|
fclose(fp1);
|
|
return res;
|
|
}
|
|
|
|
static int h_archiver(int argc, unsigned char **argv){
|
|
int j;
|
|
|
|
conf->archiver = malloc(argc * sizeof(char *));
|
|
if(conf->archiver) {
|
|
conf->archiverc = argc;
|
|
for(j = 0; j < conf->archiverc; j++) conf->archiver[j] = (unsigned char *)strdup((char *)argv[j]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int h_counter(int argc, unsigned char **argv){
|
|
struct counter_header ch1;
|
|
if(conf->counterd >=0)close(conf->counterd);
|
|
conf->counterd = open((char *)argv[1], O_BINARY|O_RDWR|O_CREAT, 0660);
|
|
if(conf->counterd<0){
|
|
fprintf(stderr, "Unable to open counter file %s, line %d\n", argv[1], linenum);
|
|
return 1;
|
|
}
|
|
if(read(conf->counterd, &ch1, sizeof(ch1))==sizeof(ch1)){
|
|
if(memcmp(&ch1, &cheader, 4)){
|
|
fprintf(stderr, "Not a counter file %s, line %d\n", argv[1], linenum);
|
|
return 2;
|
|
}
|
|
if(ch1.updated < 0 || ch1.updated >= MAX_COUNTER_TIME){
|
|
fprintf(stderr, "Invalid or corrupted counter file %s. Use countersutil utility to convert from older version\n", argv[1]);
|
|
return 3;
|
|
}
|
|
cheader.updated = ch1.updated;
|
|
}
|
|
if(argc >=4) {
|
|
conf->countertype = getrotate(*argv[2]);
|
|
if(conf->counterfile) free(conf->counterfile);
|
|
conf->counterfile = strdup((char *)argv[3]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int h_rotate(int argc, unsigned char **argv){
|
|
conf->rotate = atoi((char *)argv[1]);
|
|
return 0;
|
|
}
|
|
|
|
static int h_maxseg(int argc, unsigned char **argv){
|
|
conf->maxseg = atoi((char *)argv[1]);
|
|
return 0;
|
|
}
|
|
|
|
static int h_logformat(int argc, unsigned char **argv){
|
|
unsigned char * old = conf->logformat;
|
|
conf->logformat = (unsigned char *)strdup((char *)argv[1]);
|
|
if(old) free(old);
|
|
return 0;
|
|
}
|
|
|
|
static int h_timeouts(int argc, unsigned char **argv){
|
|
int j;
|
|
|
|
for(j = 0; conf->timeouts[j] && j + 1 < argc; j++) {
|
|
if((conf->timeouts[j] = atoi((char *)argv[j + 1])) <= 0 || conf->timeouts[j] > 2000000){
|
|
fprintf(stderr, "Invalid timeout: %s, line %d\n", argv[j + 1], linenum);
|
|
return(1);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int h_noop(int argc, unsigned char **argv){
|
|
return 0;
|
|
}
|
|
|
|
static int h_auth(int argc, unsigned char **argv){
|
|
struct auth *au, * newau;
|
|
|
|
freeauth(conf->authfuncs);
|
|
conf->authfuncs = NULL;
|
|
if(!conf->bandlimfunc)conf->bandlimfunc = bandlimitfunc;
|
|
for(argc--; argc; argc--){
|
|
for(au = authfuncs; au; au=au->next){
|
|
if(!strcmp((char *)argv[argc], au->desc)){
|
|
newau = malloc(sizeof(struct auth));
|
|
if(!newau) {
|
|
return 21;
|
|
}
|
|
newau->next = conf->authfuncs;
|
|
conf->authfuncs = newau;
|
|
conf->authfuncs->desc = au->desc;
|
|
conf->authfuncs->authenticate = au->authenticate;
|
|
conf->authfuncs->authorize = au->authorize;
|
|
break;
|
|
}
|
|
}
|
|
if(!au) return 1;
|
|
}
|
|
conf->authfunc = doauth;
|
|
return 0;
|
|
}
|
|
|
|
static int h_users(int argc, unsigned char **argv){
|
|
int j;
|
|
unsigned char *arg;
|
|
char *pw[2];
|
|
char pass[256];
|
|
int l;
|
|
|
|
for (j = 1; j < argc; j++) {
|
|
arg = (unsigned char *)strchr((char *)argv[j], ':');
|
|
if (!arg) continue;
|
|
*arg = 0;
|
|
pw[0] = (char *)argv[j];
|
|
|
|
if (!conf->pwl_table.ihashtable){
|
|
conf->pwl_table = pwl_template;
|
|
if(inithashtable(&conf->pwl_table, 16, 32, 1048576)) return 3;
|
|
}
|
|
memset(pass, 0, sizeof(pass));
|
|
if (arg[1] && arg[2] && arg[3] == ':') {
|
|
pw[1] = (char *)(arg + 4);
|
|
if (arg[1] == 'N' && arg[2] == 'T') {
|
|
#ifdef WITH_SSL
|
|
*pass = NT;
|
|
#else
|
|
continue;
|
|
#endif
|
|
}
|
|
else if (arg[1] == 'C' && arg[2] == 'R') {
|
|
*pass = CR;
|
|
}
|
|
else if (arg[1] == 'C' && arg[2] == 'L') {
|
|
*pass = CL;
|
|
} else {
|
|
continue;
|
|
}
|
|
} else {
|
|
*pass = CL;
|
|
pw[1] = (char *)(arg + 1);
|
|
}
|
|
l = strlen(pw[1]);
|
|
if(l > 255) l = 255;
|
|
if((unsigned)l >= conf->pwl_table.recsize) {
|
|
mdh_ctx *bctx;
|
|
unsigned hashsz;
|
|
unsigned int blen;
|
|
if(*pass != CL) continue;
|
|
hashsz = conf->pwl_table.recsize - 1 < 64 ? conf->pwl_table.recsize - 1 : 64;
|
|
bctx = mdh_init(MDH_BLAKE2, hashsz);
|
|
if(!bctx) continue;
|
|
mdh_update(bctx, pw[1], l + 1);
|
|
blen = hashsz;
|
|
mdh_final(bctx, (unsigned char *)pass+1, &blen);
|
|
mdh_free(bctx);
|
|
} else {
|
|
memcpy(pass + 1, pw[1], l);
|
|
}
|
|
hashadd(&conf->pwl_table, pw[0], pass, MAX_COUNTER_TIME);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int h_maxconn(int argc, unsigned char **argv){
|
|
conf->maxchild = atoi((char *)argv[1]);
|
|
if(!conf->maxchild) {
|
|
return(1);
|
|
}
|
|
#ifndef _WIN32
|
|
{
|
|
struct rlimit rl;
|
|
if(!getrlimit(RLIMIT_NOFILE, &rl)){
|
|
if((conf->maxchild<<1) > rl.rlim_cur)
|
|
fprintf(stderr, "[line %d] Warning: current open file ulimits are too low (cur: %d/max: %d),"
|
|
" maxconn requires at least %d for every running service."
|
|
" Configure ulimits according to system documentation\n",
|
|
linenum, (int)rl.rlim_cur, (int)rl.rlim_max, (conf->maxchild<<1));
|
|
}
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int h_backlog(int argc, unsigned char **argv){
|
|
conf->backlog = atoi((char *)argv[1]);
|
|
if(conf->backlog < 0) {
|
|
return(1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int h_flush(int argc, unsigned char **argv){
|
|
freeacl(conf->acl);
|
|
conf->acl = NULL;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
static int h_flushusers(int argc, unsigned char **argv){
|
|
freepwl(conf->pwl);
|
|
conf->pwl = NULL;
|
|
return 0;
|
|
}
|
|
*/
|
|
|
|
static int h_nserver(int argc, unsigned char **argv){
|
|
char *str;
|
|
|
|
if(conf->numservers < MAXNSERVERS) {
|
|
if((str = strchr((char *)argv[1], '/')))
|
|
*str = 0;
|
|
*SAPORT(&conf->nservers[conf->numservers].addr) = htons(53);
|
|
if(parsehost(46, argv[1], (struct sockaddr *)&conf->nservers[conf->numservers].addr)) return 1;
|
|
if(str) {
|
|
conf->nservers[conf->numservers].usetcp = strstr(str + 1, "tcp")? 1:0;
|
|
*str = '/';
|
|
}
|
|
conf->numservers++;
|
|
|
|
}
|
|
conf->resolvfunc = myresolver;
|
|
return 0;
|
|
}
|
|
|
|
static int h_authnserver(int argc, unsigned char **argv){
|
|
char *str;
|
|
|
|
if((str = strchr((char *)argv[1], '/')))
|
|
*str = 0;
|
|
if(parsehost(46, argv[1], (struct sockaddr *)&conf->authnserver.addr)) return 1;
|
|
*SAPORT(&conf->authnserver.addr) = htons(53);
|
|
if(str) {
|
|
conf->authnserver.usetcp = strstr(str + 1, "tcp")? 1:0;
|
|
*str = '/';
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int h_fakeresolve(int argc, unsigned char **argv){
|
|
conf->resolvfunc = fakeresolver;
|
|
return 0;
|
|
}
|
|
|
|
static int h_nscache(int argc, unsigned char **argv){
|
|
unsigned res;
|
|
|
|
res = (unsigned)atoi((char *)argv[1]);
|
|
if(res < 256) {
|
|
fprintf(stderr, "Invalid NS cache size: %d\n", res);
|
|
return 1;
|
|
}
|
|
if(dns_table.growlimit != res && inithashtable(&dns_table, (res >> 2), (res >> 2), res)){
|
|
fprintf(stderr, "Failed to initialize NS cache\n");
|
|
return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int h_parentretries(int argc, unsigned char **argv){
|
|
int res;
|
|
|
|
res = atoi((char *)argv[1]);
|
|
if(res > 0) conf->parentretries = res;
|
|
return 0;
|
|
}
|
|
|
|
static int h_nscache6(int argc, unsigned char **argv){
|
|
unsigned res;
|
|
|
|
res = (unsigned)atoi((char *)argv[1]);
|
|
if(res < 256) {
|
|
fprintf(stderr, "Invalid NS cache size: %d\n", res);
|
|
return 1;
|
|
}
|
|
if(dns6_table.growlimit != res &&inithashtable(&dns6_table, (res>>2), (res>>2), res)){
|
|
fprintf(stderr, "Failed to initialize NS cache\n");
|
|
return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int h_nsrecord(int argc, unsigned char **argv){
|
|
PROXYSOCKADDRTYPE sa;
|
|
memset(&sa, 0, sizeof(sa));
|
|
if(!getip46(46, argv[2], (struct sockaddr *)&sa)) return 1;
|
|
|
|
hashadd(*SAFAMILY(&sa)==AF_INET6?&dns6_table:&dns_table, argv[1], SAADDR(&sa), (time_t)0xffffffff);
|
|
return 0;
|
|
}
|
|
|
|
/* dialer: running a program when a name does not resolve is gone; the
|
|
command is still taken, and ignored, so configurations keep loading */
|
|
static int h_dialer(int argc, unsigned char **argv){
|
|
return 0;
|
|
}
|
|
|
|
static int h_system(int argc, unsigned char **argv){
|
|
int res;
|
|
|
|
if((res = system((char *)argv[1])) == -1){
|
|
fprintf(stderr, "Failed to start %s\n", argv[1]);
|
|
return(1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int h_pidfile(int argc, unsigned char **argv){
|
|
FILE *pidf;
|
|
|
|
if(!(pidf = fopen((char *)argv[1], "w"))){
|
|
fprintf(stderr, "Failed to open pid file %s\n", argv[1]);
|
|
return(1);
|
|
}
|
|
fprintf(pidf,"%u", (unsigned)getpid());
|
|
fclose(pidf);
|
|
return 0;
|
|
}
|
|
|
|
static int h_monitor(int argc, unsigned char **argv){
|
|
struct filemon * fm;
|
|
|
|
fm = malloc(sizeof (struct filemon));
|
|
if(!fm) return 21;
|
|
if(stat((char *)argv[1], &fm->sb)){
|
|
free(fm);
|
|
fprintf(stderr, "Warning: file %s doesn't exist on line %d\n", argv[1], linenum);
|
|
}
|
|
else {
|
|
fm->path = strdup((char *)argv[1]);
|
|
if(!fm->path) return 21;
|
|
fm->next = conf->fmon;
|
|
conf->fmon = fm;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
struct redirdesc redirs[] = {
|
|
{R_TCP, "tcp", tcppmchild},
|
|
{R_CONNECT, "connect", proxychild},
|
|
{R_SOCKS4, "socks4", sockschild},
|
|
{R_SOCKS5, "socks5", sockschild},
|
|
{R_HTTP, "http", proxychild},
|
|
{R_POP3, "pop3", pop3pchild},
|
|
{R_IMAP, "imap", imappchild},
|
|
{R_SMTP, "smtp", smtppchild},
|
|
{R_FTP, "ftp", ftpprchild},
|
|
{R_CONNECTP, "connect+", proxychild},
|
|
{R_SOCKS4P, "socks4+", sockschild},
|
|
{R_SOCKS5P, "socks5+", sockschild},
|
|
{R_SOCKS4B, "socks4b", sockschild},
|
|
{R_SOCKS5B, "socks5b", sockschild},
|
|
{R_EXTIP, "extip", NULL},
|
|
{R_EXTPORT, "extport", NULL},
|
|
{R_INTPORT, "intport", NULL},
|
|
{R_TLS, "tls", tlsprchild},
|
|
{R_HA, "ha", NULL},
|
|
{R_DNS, "dns", dnsprchild},
|
|
{0, NULL, NULL}
|
|
};
|
|
|
|
#ifdef WITH_HTTPSRV
|
|
/* Headers a rule adds are written as one argument, the lines separated by a
|
|
backslash and an n, because a configuration line cannot hold a line ending.
|
|
Those two characters become a real one here. A line ending which reached the
|
|
argument as itself is dropped: what goes on the wire is decided here and not
|
|
by whatever produced the string. */
|
|
static unsigned char * parsehdrs(const unsigned char *arg)
|
|
{
|
|
unsigned char *out, *o;
|
|
const unsigned char *p;
|
|
size_t len = strlen((char *)arg);
|
|
|
|
out = malloc(len * 2 + 3);
|
|
if(!out) return NULL;
|
|
for(p = arg, o = out; *p; p++){
|
|
if(*p == '\\' && p[1] == 'n'){
|
|
*o++ = '\r';
|
|
*o++ = '\n';
|
|
p++;
|
|
continue;
|
|
}
|
|
if(*p == '\r' || *p == '\n') continue;
|
|
*o++ = *p;
|
|
}
|
|
if(o == out || o[-1] != '\n'){
|
|
*o++ = '\r';
|
|
*o++ = '\n';
|
|
}
|
|
*o = 0;
|
|
return out;
|
|
}
|
|
|
|
/* An optional argument which a star, or nothing at all, leaves at its
|
|
default. */
|
|
static int optnum(int argc, unsigned char **argv, int at, int def)
|
|
{
|
|
if(argc <= at || !strcmp((char *)argv[at], "*")) return def;
|
|
return atoi((char *)argv[at]);
|
|
}
|
|
|
|
static void freehttprule(struct httprule *rule)
|
|
{
|
|
if(rule->host.name) free(rule->host.name);
|
|
if(rule->url.name) free(rule->url.name);
|
|
if(rule->params) free(rule->params);
|
|
if(rule->ctype) free(rule->ctype);
|
|
if(rule->hdrs) free(rule->hdrs);
|
|
free(rule);
|
|
}
|
|
|
|
/* Installs one rule from code, for the pages a service predefines. */
|
|
static int addhttprule(char *op, char *host, char *url, char *params)
|
|
{
|
|
struct httprule *rule, *tail;
|
|
unsigned char hostbuf[64], urlbuf[128];
|
|
|
|
rule = malloc(sizeof(struct httprule));
|
|
if(!rule) return 1;
|
|
memset(rule, 0, sizeof(struct httprule));
|
|
rule->maxage = -1;
|
|
|
|
rule->op = httpopbyname((unsigned char *)op);
|
|
if(rule->op < 0){
|
|
free(rule);
|
|
return 1;
|
|
}
|
|
|
|
strcpy((char *)hostbuf, host);
|
|
strcpy((char *)urlbuf, url);
|
|
if(parsepattern(&rule->host, hostbuf) || parsepathpattern(&rule->url, urlbuf)){
|
|
free(rule->host.name);
|
|
free(rule);
|
|
return 1;
|
|
}
|
|
if(params) rule->params = (unsigned char *)strdup(params);
|
|
|
|
if(!conf->httprules) conf->httprules = rule;
|
|
else {
|
|
for(tail = conf->httprules; tail->next; tail = tail->next);
|
|
tail->next = rule;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/* Parses an inclusive FIRST-LAST local port range into first | last << 16. */
|
|
static int parserange(unsigned char *arg, uint32_t *range)
|
|
{
|
|
char *end;
|
|
unsigned long first, last;
|
|
|
|
first = strtoul((char *)arg, &end, 10);
|
|
if(end == (char *)arg || *end != '-' || !first || first > 65535) return 1;
|
|
|
|
arg = (unsigned char *)end + 1;
|
|
last = strtoul((char *)arg, &end, 10);
|
|
if(end == (char *)arg || *end || !last || last > 65535 || last < first) return 1;
|
|
|
|
*range = (uint32_t)first | ((uint32_t)last << 16);
|
|
return 0;
|
|
}
|
|
|
|
/* Scan a parent weight into its share of WEIGHTSCALE, as an integer: there is
|
|
* no floating point anywhere near a configuration file.
|
|
*
|
|
* A weight starting with 0 or . is a fraction of one, so .333 and 0.333 are
|
|
* both a third. Anything else is the old notation, thousandths, where 1000 is
|
|
* the whole share, and it may now carry more digits after a dot: 123.456 means
|
|
* the same as .123456. Either way at most 9 digits are kept, which is the
|
|
* resolution weights are held at.
|
|
*
|
|
* 1 followed by a point and nothing but zeroes is the one weight read as it
|
|
* looks rather than as thousandths: 1.0 is the whole share, where a bare 1 is
|
|
* a thousandth of it.
|
|
*
|
|
* A weight may also be written as a percentage, which is what a trailing %
|
|
* makes it: 50.5% is .505 is 505.
|
|
*/
|
|
static int parseweight(unsigned char * s, unsigned * weight){
|
|
static const unsigned pow10[10] = {1, 10, 100, 1000, 10000, 100000,
|
|
1000000, 10000000, 100000000, 1000000000};
|
|
unsigned char *p, *end;
|
|
uint64_t val = 0, res;
|
|
int ndigits = 0, atpoint = -1, after, percent = 0, fraction;
|
|
|
|
if(!s || !*s) return 1;
|
|
end = s + strlen((char *)s);
|
|
if(end[-1] == '%'){
|
|
percent = 1;
|
|
if(--end == s) return 1;
|
|
}
|
|
/* 1.0, with as many zeroes after it as anyone cares to write, is the one
|
|
weight read as it looks rather than as thousandths: the whole share,
|
|
where a bare 1 is a thousandth of it */
|
|
if(!percent && s[0] == '1' && s[1] == '.' && s[2]){
|
|
for(p = s + 2; *p == '0'; p++);
|
|
if(!*p){
|
|
*weight = WEIGHTSCALE;
|
|
return 0;
|
|
}
|
|
}
|
|
fraction = (*s == '.' || *s == '0');
|
|
for(p = s; p < end; p++){
|
|
if(*p == '.'){
|
|
if(atpoint >= 0) return 1;
|
|
atpoint = ndigits;
|
|
continue;
|
|
}
|
|
if(*p < '0' || *p > '9') return 1;
|
|
if(ndigits == 18) return 1;
|
|
val = (val * 10) + (unsigned)(*p - '0');
|
|
ndigits++;
|
|
}
|
|
if(!ndigits || val > WEIGHTSCALE) return 1;
|
|
after = (atpoint < 0)? 0 : ndigits - atpoint;
|
|
if(percent){
|
|
/* a hundredth of the whole share for every 1% */
|
|
if(after > 7) return 1;
|
|
res = val * pow10[7 - after];
|
|
}
|
|
else if(fraction){
|
|
/* the digits before the point are the leading zero and add
|
|
nothing, so only the ones after it say what the share is */
|
|
if(atpoint < 0) return val? 1 : (*weight = 0, 0);
|
|
if(after > 9) return 1;
|
|
res = val * pow10[9 - after];
|
|
}
|
|
else {
|
|
/* thousandths, with the digits after the point carrying on
|
|
from them: three digits of a whole share, six more after */
|
|
if(after > 6) return 1;
|
|
res = val * pow10[6 - after];
|
|
}
|
|
if(res > WEIGHTSCALE) return 1;
|
|
*weight = (unsigned)res;
|
|
return 0;
|
|
}
|
|
|
|
static int h_parent(int argc, unsigned char **argv){
|
|
struct ace *acl = NULL;
|
|
struct chain *chains;
|
|
char * cidr = NULL;
|
|
int i;
|
|
|
|
acl = conf->acl;
|
|
while(acl && acl->next) acl = acl->next;
|
|
if(!acl || (acl->action && acl->action != 2)) {
|
|
fprintf(stderr, "Chaining error: last ACL entry was not \"allow\" or \"redirect\" on line %d\n", linenum);
|
|
return(1);
|
|
}
|
|
acl->action = 2;
|
|
|
|
chains = malloc(sizeof(struct chain));
|
|
if(!chains){
|
|
return(21);
|
|
}
|
|
memset(chains, 0, sizeof(struct chain));
|
|
/* 0 is the fallback weight: such a parent is only used once every
|
|
weighted parent of its group has failed */
|
|
if(parseweight(argv[1], &chains->weight)) {
|
|
fprintf(stderr, "Chaining error: bad chain weight %s line %d\n", argv[1], linenum);
|
|
free(chains);
|
|
return(3);
|
|
}
|
|
for(i = 0; redirs[i].name ; i++){
|
|
int len;
|
|
len = strlen(redirs[i].name);
|
|
if(!strncmp((char *)argv[2], redirs[i].name, len)
|
|
&& (argv[2][len] == 0 || (argv[2][len] == 's' && argv[2][len+1] == 0))
|
|
) {
|
|
chains->type = redirs[i].redir;
|
|
if(argv[2][len] == 's') chains->secure = 1;
|
|
break;
|
|
}
|
|
}
|
|
if(!redirs[i].name) {
|
|
fprintf(stderr, "Chaining error: bad chain type (%s)\n", argv[2]);
|
|
free(chains);
|
|
return(4);
|
|
}
|
|
#ifdef WITH_UN
|
|
if(!strncmp((char *)argv[3], "unix:", 5)){
|
|
make_un(argv[3] + 5, (struct sockaddr_un*)&chains->addr);
|
|
}
|
|
else {
|
|
#endif
|
|
cidr = strchr((char *)argv[3], '/');
|
|
if(cidr) *cidr = 0;
|
|
if(!getip46(46, argv[3], (struct sockaddr *)&chains->addr)) {
|
|
free(chains);
|
|
return (5);
|
|
}
|
|
#ifdef WITH_UN
|
|
}
|
|
#endif
|
|
chains->exthost = (unsigned char *)strdup((char *)argv[3]);
|
|
if(!chains->exthost) {
|
|
free(chains);
|
|
return 21;
|
|
}
|
|
if(cidr){
|
|
*cidr = '/';
|
|
chains->cidr = atoi(cidr + 1);
|
|
}
|
|
if(chains->type == R_EXTPORT || chains->type == R_INTPORT){
|
|
if(!SAISNULL(&chains->addr)){
|
|
fprintf(stderr, "Chaining error: chain type (%s) sets a local port range, it requires 0.0.0.0 as address on line %d\n", argv[2], linenum);
|
|
free(chains->exthost);
|
|
free(chains);
|
|
return(4);
|
|
}
|
|
if(parserange(argv[4], &chains->range)){
|
|
fprintf(stderr, "Chaining error: bad port range (%s) on line %d\n", argv[4], linenum);
|
|
free(chains->exthost);
|
|
free(chains);
|
|
return(3);
|
|
}
|
|
}
|
|
else *SAPORT(&chains->addr) = htons((uint16_t)atoi((char *)argv[4]));
|
|
switch(chains->type){
|
|
case R_POP3:
|
|
case R_SMTP:
|
|
case R_FTP:
|
|
case R_ADMIN:
|
|
case R_TLS:
|
|
case R_DNS:
|
|
case R_HA:
|
|
if(!SAISNULL(&chains->addr) || *SAPORT(&chains->addr)){
|
|
fprintf(stderr, "Chaining error: chain type (%s) is a local redirection, it requires 0.0.0.0 as address and 0 as port on line %d\n", argv[2], linenum);
|
|
free(chains->exthost);
|
|
free(chains);
|
|
return(4);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if(argc > 5) chains->extuser = (unsigned char *)strdup((char *)argv[5]);
|
|
if(argc > 6) chains->extpass = (unsigned char *)strdup((char *)argv[6]);
|
|
if(!acl->chains) {
|
|
acl->chains = chains;
|
|
}
|
|
else {
|
|
struct chain *tmpchain;
|
|
|
|
for(tmpchain = acl->chains; tmpchain->next; tmpchain = tmpchain->next);
|
|
tmpchain->next = chains;
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
|
|
#ifdef WITH_HTTPSRV
|
|
/* http <hostname> <url> <operation> [parameters]
|
|
Rules are matched in the order they are given, first match wins. */
|
|
static int h_http(int argc, unsigned char **argv){
|
|
struct httprule *rule, *tail;
|
|
int op;
|
|
|
|
/* http OPERATION HOST URL [PARAMETERS] */
|
|
op = httpopbyname(argv[1]);
|
|
if(op < 0){
|
|
fprintf(stderr, "Unknown http operation: %s line %d\n", argv[1], linenum);
|
|
return(1);
|
|
}
|
|
|
|
rule = malloc(sizeof(struct httprule));
|
|
if(!rule) return(21);
|
|
memset(rule, 0, sizeof(struct httprule));
|
|
rule->op = op;
|
|
rule->maxage = -1;
|
|
|
|
if(parsepattern(&rule->host, argv[2]) || parsepathpattern(&rule->url, argv[3])){
|
|
fprintf(stderr, "No memory for http rule, line %d\n", linenum);
|
|
free(rule->host.name);
|
|
free(rule);
|
|
return(21);
|
|
}
|
|
|
|
if(argc > 4 && (!strcmp((char *)argv[1], "file") || !strcmp((char *)argv[1], "cache"))){
|
|
/* PATH [TYPE [MAX-AGE [HEADERS [CODE]]]]. A star, or nothing,
|
|
leaves each of them out: the type is worked out from the name,
|
|
nothing is said about caching, no headers are added and the
|
|
answer is the usual 200. */
|
|
rule->params = (unsigned char *)strdup((char *)argv[4]);
|
|
if(argc > 5 && strcmp((char *)argv[5], "*"))
|
|
rule->ctype = (unsigned char *)strdup((char *)argv[5]);
|
|
rule->maxage = optnum(argc, argv, 6, -1);
|
|
if(argc > 7 && strcmp((char *)argv[7], "*"))
|
|
rule->hdrs = parsehdrs(argv[7]);
|
|
rule->code = optnum(argc, argv, 8, 0);
|
|
if(!rule->params
|
|
|| (argc > 5 && strcmp((char *)argv[5], "*") && !rule->ctype)
|
|
|| (argc > 7 && strcmp((char *)argv[7], "*") && !rule->hdrs)){
|
|
freehttprule(rule);
|
|
return(21);
|
|
}
|
|
}
|
|
else if(!strcmp((char *)argv[1], "reply")){
|
|
/* CODE [HEADERS], and no body at all. */
|
|
rule->code = optnum(argc, argv, 4, 200);
|
|
if(argc > 5 && strcmp((char *)argv[5], "*")){
|
|
rule->hdrs = parsehdrs(argv[5]);
|
|
if(!rule->hdrs){
|
|
freehttprule(rule);
|
|
return(21);
|
|
}
|
|
}
|
|
}
|
|
else if(argc > 4){
|
|
/* What follows the URL belongs to the operation, and an operation
|
|
such as redir reads more than one word of it. */
|
|
int i, len = 0;
|
|
|
|
for(i = 4; i < argc; i++) len += (int)strlen((char *)argv[i]) + 1;
|
|
rule->params = malloc(len);
|
|
if(rule->params){
|
|
int at = 0;
|
|
|
|
for(i = 4; i < argc; i++)
|
|
at += sprintf((char *)rule->params + at, "%s%s",
|
|
i > 4? " " : "", argv[i]);
|
|
}
|
|
if(!rule->params){
|
|
freehttprule(rule);
|
|
return(21);
|
|
}
|
|
}
|
|
|
|
if(rule->code && (rule->code < 100 || rule->code > 599)){
|
|
fprintf(stderr, "Wrong http status: %d line %d\n", rule->code, linenum);
|
|
freehttprule(rule);
|
|
return(1);
|
|
}
|
|
if(rule->maxage < -1){
|
|
fprintf(stderr, "Wrong max-age, line %d\n", linenum);
|
|
freehttprule(rule);
|
|
return(1);
|
|
}
|
|
|
|
if(!conf->httprules) conf->httprules = rule;
|
|
else {
|
|
for(tail = conf->httprules; tail->next; tail = tail->next);
|
|
tail->next = rule;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int h_nolog(int argc, unsigned char **argv){
|
|
struct ace *acl = NULL;
|
|
|
|
acl = conf->acl;
|
|
if(!acl) {
|
|
fprintf(stderr, "Chaining error: last ACL entry was not \"allow/deny\" on line %d\n", linenum);
|
|
return(1);
|
|
}
|
|
while(acl->next) acl = acl->next;
|
|
if(argc == 1) acl->nolog = 1;
|
|
else acl->weight = atoi((char*)argv[1]);
|
|
return 0;
|
|
}
|
|
|
|
int scanipl(unsigned char *arg, struct iplist *dst){
|
|
PROXYSOCKADDRTYPE sa;
|
|
char * slash, *dash;
|
|
int masklen, addrlen;
|
|
int res;
|
|
|
|
if((slash = strchr((char *)arg, '/'))) *slash = 0;
|
|
if((dash = strchr((char *)arg,'-'))) *dash = 0;
|
|
|
|
if(afdetect(arg) == -1) {
|
|
if(slash)*slash = '/';
|
|
if(dash)*dash = '-';
|
|
return 1;
|
|
}
|
|
res = getip46(46, arg, (struct sockaddr *)&sa);
|
|
if(dash)*dash = '-';
|
|
if(!res) return 1;
|
|
memcpy(&dst->ip_from, SAADDR(&sa), SAADDRLEN(&sa));
|
|
dst->family = *SAFAMILY(&sa);
|
|
if(dash){
|
|
if(afdetect((unsigned char *)dash+1) == -1) return 1;
|
|
if(!getip46(46, (unsigned char *)dash+1, (struct sockaddr *)&sa)) return 2;
|
|
memcpy(&dst->ip_to, SAADDR(&sa), SAADDRLEN(&sa));
|
|
if(*SAFAMILY(&sa) != dst->family || memcmp(&dst->ip_to, &dst->ip_from, SAADDRLEN(&sa)) < 0) return 3;
|
|
return 0;
|
|
}
|
|
memcpy(&dst->ip_to, &dst->ip_from, SAADDRLEN(&sa));
|
|
if(slash){
|
|
*slash = '/';
|
|
addrlen = SAADDRLEN(&sa);
|
|
masklen = atoi(slash+1);
|
|
if(masklen < 0 || masklen > (addrlen*8)) return 4;
|
|
else {
|
|
int i, nbytes = masklen / 8, nbits = (8 - (masklen % 8)) % 8;
|
|
|
|
for(i = addrlen; i>(nbytes + (nbits > 0)); i--){
|
|
((unsigned char *)&dst->ip_from)[i-1] = 0x00;
|
|
((unsigned char *)&dst->ip_to)[i-1] = 0xff;
|
|
}
|
|
for(;nbits;nbits--){
|
|
((unsigned char *)&dst->ip_from)[nbytes] &= ~(0x01<<(nbits-1));
|
|
((unsigned char *)&dst->ip_to)[nbytes] |= (0x01<<(nbits-1));
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
struct ace * make_ace (int argc, unsigned char ** argv){
|
|
struct ace * acl;
|
|
unsigned char *arg;
|
|
struct iplist *ipl=NULL;
|
|
struct portlist *portl=NULL;
|
|
struct userlist *userl=NULL;
|
|
struct hostname *hostnamel=NULL;
|
|
int res;
|
|
|
|
acl = malloc(sizeof(struct ace));
|
|
if(!acl) return acl;
|
|
memset(acl, 0, sizeof(struct ace));
|
|
if(argc > 0 && strcmp("*", (char *)argv[0])) {
|
|
arg = argv[0];
|
|
arg = (unsigned char *)strtok((char *)arg, ",");
|
|
if(arg) do {
|
|
if(!acl->users) {
|
|
acl->users = userl = malloc(sizeof(struct userlist));
|
|
}
|
|
else {
|
|
userl->next = malloc(sizeof(struct userlist));
|
|
userl = userl -> next;
|
|
}
|
|
if(!userl) {
|
|
fprintf(stderr, "No memory for ACL entry, line %d\n", linenum);
|
|
return(NULL);
|
|
}
|
|
memset(userl, 0, sizeof(struct userlist));
|
|
userl->user=(unsigned char*)strdup((char *)arg);
|
|
if(!userl->user) return NULL;
|
|
} while((arg = (unsigned char *)strtok((char *)NULL, ",")));
|
|
}
|
|
if(argc > 1 && strcmp("*", (char *)argv[1])) {
|
|
arg = (unsigned char *)strtok((char *)argv[1], ",");
|
|
if(arg) do {
|
|
if(!acl->src) {
|
|
acl->src = ipl = malloc(sizeof(struct iplist));
|
|
}
|
|
else {
|
|
ipl->next = malloc(sizeof(struct iplist));
|
|
ipl = ipl -> next;
|
|
}
|
|
if(!ipl) {
|
|
fprintf(stderr, "No memory for ACL entry, line %d\n", linenum);
|
|
return(NULL);
|
|
}
|
|
memset(ipl, 0, sizeof(struct iplist));
|
|
if (scanipl(arg, ipl)) {
|
|
fprintf(stderr, "Invalid IP, IP range or CIDR, line %d\n", linenum);
|
|
return(NULL);
|
|
}
|
|
} while((arg = (unsigned char *)strtok((char *)NULL, ",")));
|
|
}
|
|
if(argc > 2 && strcmp("*", (char *)argv[2])) {
|
|
arg = (unsigned char *)strtok((char *)argv[2], ",");
|
|
if(arg) do {
|
|
int arglen;
|
|
struct iplist tmpip={NULL};
|
|
|
|
arglen = (int)strlen((char *)arg);
|
|
if(scanipl(arg, &tmpip)){
|
|
if(!arglen) continue;
|
|
if(!acl->dstnames) {
|
|
acl->dstnames = hostnamel = malloc(sizeof(struct hostname));
|
|
}
|
|
else {
|
|
hostnamel->next = malloc(sizeof(struct hostname));
|
|
hostnamel = hostnamel -> next;
|
|
}
|
|
if(!hostnamel){
|
|
fprintf(stderr, "No memory for ACL entry, line %d\n", linenum);
|
|
return(NULL);
|
|
}
|
|
memset(hostnamel, 0, sizeof(struct hostname));
|
|
if(parsepattern(hostnamel, arg)) {
|
|
fprintf(stderr, "No memory for ACL entry, line %d\n", linenum);
|
|
return(NULL);
|
|
}
|
|
}
|
|
else {
|
|
|
|
if(!acl->dst) {
|
|
acl->dst = ipl = malloc(sizeof(struct iplist));
|
|
}
|
|
else {
|
|
ipl->next = malloc(sizeof(struct iplist));
|
|
ipl = ipl -> next;
|
|
}
|
|
if(!ipl) {
|
|
fprintf(stderr, "No memory for ACL entry, line %d\n", linenum);
|
|
return(NULL);
|
|
}
|
|
*ipl = tmpip;
|
|
}
|
|
}while((arg = (unsigned char *)strtok((char *)NULL, ",")));
|
|
}
|
|
if(argc > 3 && strcmp("*", (char *)argv[3])) {
|
|
arg = (unsigned char *)strtok((char *)argv[3], ",");
|
|
if(arg) do {
|
|
if(!acl->ports) {
|
|
acl->ports = portl = malloc(sizeof(struct portlist));
|
|
}
|
|
else {
|
|
portl->next = malloc(sizeof(struct portlist));
|
|
portl = portl -> next;
|
|
}
|
|
if(!portl) {
|
|
fprintf(stderr, "No memory for ACL entry, line %d\n", linenum);
|
|
return(NULL);
|
|
}
|
|
memset(portl, 0, sizeof(struct portlist));
|
|
res = sscanf((char *)arg, "%hu-%hu", &portl->startport, &portl->endport);
|
|
if(res < 1) {
|
|
fprintf(stderr, "Invalid port or port range, line %d\n", linenum);
|
|
return(NULL);
|
|
}
|
|
if (res == 1) portl->endport = portl->startport;
|
|
} while((arg = (unsigned char *)strtok((char *)NULL, ",")));
|
|
}
|
|
if(argc > 4 && strcmp("*", (char *)argv[4])) {
|
|
arg = (unsigned char *)strtok((char *)argv[4], ",");
|
|
if(arg) do {
|
|
if(!strcmp((char *)arg, "CONNECT")){
|
|
acl->operation |= CONNECT;
|
|
}
|
|
else if(!strcmp((char *)arg, "BIND")){
|
|
acl->operation |= BIND;
|
|
}
|
|
else if(!strcmp((char *)arg, "UDPASSOC")){
|
|
acl->operation |= UDPASSOC;
|
|
}
|
|
else if(!strcmp((char *)arg, "ICMPASSOC")){
|
|
acl->operation |= ICMPASSOC;
|
|
}
|
|
else if(!strcmp((char *)arg, "HTTP_GET")){
|
|
acl->operation |= HTTP_GET;
|
|
}
|
|
else if(!strcmp((char *)arg, "HTTP_PUT")){
|
|
acl->operation |= HTTP_PUT;
|
|
}
|
|
else if(!strcmp((char *)arg, "HTTP_POST")){
|
|
acl->operation |= HTTP_POST;
|
|
}
|
|
else if(!strcmp((char *)arg, "HTTP_HEAD")){
|
|
acl->operation |= HTTP_HEAD;
|
|
}
|
|
else if(!strcmp((char *)arg, "HTTP_OTHER")){
|
|
acl->operation |= HTTP_OTHER;
|
|
}
|
|
else if(!strcmp((char *)arg, "HTTP_CONNECT")){
|
|
acl->operation |= HTTP_CONNECT;
|
|
}
|
|
else if(!strcmp((char *)arg, "HTTP")){
|
|
acl->operation |= HTTP;
|
|
}
|
|
else if(!strcmp((char *)arg, "HTTPS")){
|
|
acl->operation |= HTTPS;
|
|
}
|
|
else if(!strcmp((char *)arg, "FTP_GET")){
|
|
acl->operation |= FTP_GET;
|
|
}
|
|
else if(!strcmp((char *)arg, "FTP_PUT")){
|
|
acl->operation |= FTP_PUT;
|
|
}
|
|
else if(!strcmp((char *)arg, "FTP_LIST")){
|
|
acl->operation |= FTP_LIST;
|
|
}
|
|
else if(!strcmp((char *)arg, "FTP_DATA")){
|
|
acl->operation |= FTP_DATA;
|
|
}
|
|
else if(!strcmp((char *)arg, "FTP")){
|
|
acl->operation |= FTP;
|
|
}
|
|
else if(!strcmp((char *)arg, "ADMIN")){
|
|
acl->operation |= ADMIN;
|
|
}
|
|
else if(!strcmp((char *)arg, "DNSRESOLVE")){
|
|
acl->operation |= DNSRESOLVE;
|
|
}
|
|
else {
|
|
fprintf(stderr, "Unknown operation type: %s line %d\n", arg, linenum);
|
|
return(NULL);
|
|
}
|
|
} while((arg = (unsigned char *)strtok((char *)NULL, ",")));
|
|
}
|
|
if(argc > 5){
|
|
for(arg = argv[5]; *arg;){
|
|
int val, val1;
|
|
|
|
if(!isnumber(*arg)){
|
|
arg++;
|
|
continue;
|
|
}
|
|
val1 = val = (*arg - '0');
|
|
arg++;
|
|
if(*arg == '-' && isnumber(*(arg+1)) && (*(arg+1) - '0') > val) {
|
|
val1 = (*(arg+1) - '0');
|
|
arg+=2;
|
|
}
|
|
for(; val<=val1; val++) acl->wdays |= (1 << (val % 7));
|
|
}
|
|
|
|
}
|
|
if(argc > 6){
|
|
for(arg = argv[6]; strlen((char *)arg) >= 17 &&
|
|
isdigit(arg[0]) &&
|
|
isdigit(arg[1]) &&
|
|
isdigit(arg[3]) &&
|
|
isdigit(arg[4]) &&
|
|
isdigit(arg[6]) &&
|
|
isdigit(arg[7]) &&
|
|
isdigit(arg[9]) &&
|
|
isdigit(arg[10]) &&
|
|
isdigit(arg[12]) &&
|
|
isdigit(arg[13]) &&
|
|
isdigit(arg[15]) &&
|
|
isdigit(arg[16])
|
|
; arg+=18){
|
|
|
|
int t1, t2;
|
|
struct period *sp;
|
|
|
|
t1 = (arg[0] - '0') * 10 + (arg[1] - '0');
|
|
t1 = (t1 * 60) + (arg[3] - '0') * 10 + (arg[4] - '0');
|
|
t1 = (t1 * 60) + (arg[6] - '0') * 10 + (arg[7] - '0');
|
|
t2 = (arg[9] - '0') * 10 + (arg[10] - '0');
|
|
t2 = (t2 * 60) + (arg[12] - '0') * 10 + (arg[13] - '0');
|
|
t2 = (t2 * 60) + (arg[15] - '0') * 10 + (arg[16] - '0');
|
|
if(t2 < t1) break;
|
|
sp = malloc(sizeof(struct period));
|
|
if(sp){
|
|
sp->fromtime = t1;
|
|
sp->totime = t2;
|
|
sp->next = acl->periods;
|
|
acl->periods = sp;
|
|
}
|
|
if(arg[17]!=',') break;
|
|
}
|
|
}
|
|
if (argc > 7){
|
|
acl->weight = atoi((char *)argv[7]);
|
|
}
|
|
|
|
return acl;
|
|
}
|
|
|
|
|
|
static int h_ace(int argc, unsigned char **argv){
|
|
int res = 0;
|
|
int offset = 0;
|
|
struct ace *acl = NULL;
|
|
struct bandlim * nbl;
|
|
struct trafcount * tl;
|
|
struct connlim * ncl;
|
|
|
|
if(!strcmp((char *)argv[0], "allow")){
|
|
res = ALLOW;
|
|
}
|
|
else if(!strcmp((char *)argv[0], "deny")){
|
|
res = DENY;
|
|
}
|
|
else if(!strcmp((char *)argv[0], "redirect")){
|
|
res = REDIRECT;
|
|
offset = 2;
|
|
}
|
|
else if(!strcmp((char *)argv[0], "bandlimin")||!strcmp((char *)argv[0], "bandlimout")){
|
|
res = BANDLIM;
|
|
offset = 1;
|
|
}
|
|
else if(!strcmp((char *)argv[0], "nobandlimin")||!strcmp((char *)argv[0], "nobandlimout")){
|
|
res = NOBANDLIM;
|
|
}
|
|
else if(!strcmp((char *)argv[0], "countin")){
|
|
res = COUNTIN;
|
|
offset = 3;
|
|
}
|
|
else if(!strcmp((char *)argv[0], "nocountin")){
|
|
res = NOCOUNTIN;
|
|
}
|
|
else if(!strcmp((char *)argv[0], "countout")){
|
|
res = COUNTOUT;
|
|
offset = 3;
|
|
}
|
|
else if(!strcmp((char *)argv[0], "nocountout")){
|
|
res = NOCOUNTOUT;
|
|
}
|
|
else if(!strcmp((char *)argv[0], "countall")){
|
|
res = COUNTALL;
|
|
offset = 3;
|
|
}
|
|
else if(!strcmp((char *)argv[0], "nocountall")){
|
|
res = NOCOUNTALL;
|
|
}
|
|
else if(!strcmp((char *)argv[0], "connlim")){
|
|
res = CONNLIM;
|
|
offset = 2;
|
|
}
|
|
else if(!strcmp((char *)argv[0], "noconnlim")){
|
|
res = NOCONNLIM;
|
|
}
|
|
acl = make_ace(argc - (offset+1), argv + (offset + 1));
|
|
if(!acl) {
|
|
fprintf(stderr, "Unable to parse ACL entry, line %d\n", linenum);
|
|
return(1);
|
|
}
|
|
acl->action = res;
|
|
switch(acl->action){
|
|
case REDIRECT:
|
|
acl->chains = malloc(sizeof(struct chain));
|
|
if(!acl->chains) {
|
|
freeacl(acl);
|
|
return(21);
|
|
}
|
|
memset(acl->chains, 0, sizeof(struct chain));
|
|
acl->chains->type = R_HTTP;
|
|
if(!getip46(46, argv[1], (struct sockaddr *)&acl->chains->addr)) {
|
|
freeacl(acl);
|
|
return 5;
|
|
}
|
|
*SAPORT(&acl->chains->addr) = htons((uint16_t)atoi((char *)argv[2]));
|
|
acl->chains->weight = WEIGHTSCALE;
|
|
case ALLOW:
|
|
case DENY:
|
|
if(!conf->acl){
|
|
conf->acl = acl;
|
|
}
|
|
else {
|
|
struct ace * acei;
|
|
|
|
for(acei = conf->acl; acei->next; acei = acei->next);
|
|
acei->next = acl;
|
|
}
|
|
break;
|
|
case CONNLIM:
|
|
case NOCONNLIM:
|
|
ncl = malloc(sizeof(struct connlim));
|
|
if(!ncl) {
|
|
freeacl(acl);
|
|
return(21);
|
|
}
|
|
memset(ncl, 0, sizeof(struct connlim));
|
|
ncl->ace = acl;
|
|
if(acl->action == CONNLIM) {
|
|
sscanf((char *)argv[1], "%u", &ncl->rate);
|
|
sscanf((char *)argv[2], "%u", &ncl->period);
|
|
}
|
|
_3proxy_mutex_lock(&connlim_mutex);
|
|
if(!conf->connlimiter){
|
|
conf->connlimiter = ncl;
|
|
}
|
|
else {
|
|
struct connlim * cli;
|
|
|
|
for(cli = conf->connlimiter; cli->next; cli = cli->next);
|
|
cli->next = ncl;
|
|
}
|
|
_3proxy_mutex_unlock(&connlim_mutex);
|
|
break;
|
|
|
|
case BANDLIM:
|
|
case NOBANDLIM:
|
|
|
|
nbl = malloc(sizeof(struct bandlim));
|
|
if(!nbl) {
|
|
freeacl(acl);
|
|
return(21);
|
|
}
|
|
memset(nbl, 0, sizeof(struct bandlim));
|
|
nbl->ace = acl;
|
|
if(acl->action == BANDLIM) {
|
|
sscanf((char *)argv[1], "%u", &nbl->rate);
|
|
if(nbl->rate < 300) {
|
|
free(nbl);
|
|
freeacl(acl);
|
|
fprintf(stderr, "Wrong bandwidth specified, line %d\n", linenum);
|
|
return(4);
|
|
}
|
|
}
|
|
_3proxy_mutex_lock(&bandlim_mutex);
|
|
if(!strcmp((char *)argv[0], "bandlimin") || !strcmp((char *)argv[0], "nobandlimin")){
|
|
if(!conf->bandlimiter){
|
|
conf->bandlimiter = nbl;
|
|
}
|
|
else {
|
|
struct bandlim * bli;
|
|
|
|
for(bli = conf->bandlimiter; bli->next; bli = bli->next);
|
|
bli->next = nbl;
|
|
}
|
|
}
|
|
else {
|
|
if(!conf->bandlimiterout){
|
|
conf->bandlimiterout = nbl;
|
|
}
|
|
else {
|
|
struct bandlim * bli;
|
|
|
|
for(bli = conf->bandlimiterout; bli->next; bli = bli->next);
|
|
bli->next = nbl;
|
|
}
|
|
}
|
|
conf->bandlimver++;
|
|
_3proxy_mutex_unlock(&bandlim_mutex);
|
|
break;
|
|
|
|
case COUNTIN:
|
|
case NOCOUNTIN:
|
|
case COUNTOUT:
|
|
case NOCOUNTOUT:
|
|
case COUNTALL:
|
|
case NOCOUNTALL:
|
|
if(!conf->trafcountfunc) conf->trafcountfunc = trafcountfunc;
|
|
tl = malloc(sizeof(struct trafcount));
|
|
if(!tl) {
|
|
freeacl(acl);
|
|
return(21);
|
|
}
|
|
memset(tl, 0, sizeof(struct trafcount));
|
|
tl->ace = acl;
|
|
|
|
if((acl->action == COUNTIN)||(acl->action == COUNTOUT)||(acl->action == COUNTALL)) {
|
|
uint64_t lim = 0;
|
|
|
|
tl->comment = ( char *)argv[1];
|
|
while(isdigit(*tl->comment))tl->comment++;
|
|
if(*tl->comment== '/')tl->comment++;
|
|
tl->comment = strdup(tl->comment);
|
|
|
|
sscanf((char *)argv[1], "%u", &tl->number);
|
|
if(sscanf((char *)argv[3], "%"SCNu64"", &lim) != 1) lim = 0;
|
|
tl->type = getrotate(*argv[2]);
|
|
tl->traflim64 = lim*(1024*1024);
|
|
if(!tl->traflim64) {
|
|
free(tl);
|
|
freeacl(acl);
|
|
fprintf(stderr, "Wrong traffic limit specified, line %d\n", linenum);
|
|
return(6);
|
|
}
|
|
if(!tl->number) tl->v = &tl->own;
|
|
else if(!(tl->v = trafvalue(tl->number))){
|
|
free(tl->comment);
|
|
free(tl);
|
|
freeacl(acl);
|
|
fprintf(stderr, "Invalid, incompatible or corrupted counter file.\n");
|
|
return(6);
|
|
}
|
|
}
|
|
else tl->v = &tl->own;
|
|
_3proxy_mutex_lock(&tc_mutex);
|
|
if(!conf->trafcounter){
|
|
conf->trafcounter = tl;
|
|
}
|
|
else {
|
|
struct trafcount * ntl;
|
|
|
|
for(ntl = conf->trafcounter; ntl->next; ntl = ntl->next);
|
|
ntl->next = tl;
|
|
}
|
|
_3proxy_mutex_unlock(&tc_mutex);
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int h_logdump(int argc, unsigned char **argv){
|
|
conf->logdumpsrv = (unsigned) atoi((char *) *(argv + 1));
|
|
if(argc > 2) conf->logdumpcli = (unsigned) atoi((char *) *(argv + 2));
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int h_filtermaxsize(int argc, unsigned char **argv){
|
|
conf->filtermaxsize = atoi((char *) *(argv + 1));
|
|
return 0;
|
|
}
|
|
|
|
static int h_delimchar(int argc, unsigned char **argv){
|
|
conf->delimchar = *argv[1];
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifndef NORADIUS
|
|
static int h_radius(int argc, unsigned char **argv){
|
|
uint16_t port;
|
|
|
|
memset(conf->radiuslist, 0, sizeof(conf->radiuslist));
|
|
if(strlen((char *)argv[1]) > 63) argv[1][63] = 0;
|
|
strcpy(conf->radiussecret, (char *)argv[1]);
|
|
for( conf->nradservers=0; conf->nradservers < MAXRADIUS && conf->nradservers < argc -2; conf->nradservers++){
|
|
char *s = 0;
|
|
if((s=strchr((char *)argv[conf->nradservers + 2], '/'))){
|
|
*s = 0;
|
|
s++;
|
|
}
|
|
if( !getip46(46, argv[conf->nradservers + 2], (struct sockaddr *)&conf->radiuslist[conf->nradservers].authaddr)) return 1;
|
|
if( s && !getip46(46, (unsigned char *)s, (struct sockaddr *)&conf->radiuslist[conf->nradservers].localaddr)) return 2;
|
|
if(!*SAPORT(&conf->radiuslist[conf->nradservers].authaddr))*SAPORT(&conf->radiuslist[conf->nradservers].authaddr) = htons(1812);
|
|
port = ntohs(*SAPORT(&conf->radiuslist[conf->nradservers].authaddr));
|
|
conf->radiuslist[conf->nradservers].logaddr = conf->radiuslist[conf->nradservers].authaddr;
|
|
*SAPORT(&conf->radiuslist[conf->nradservers].logaddr) = htons(port+1);
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
static int h_authcache(int argc, unsigned char **argv){
|
|
int authcachesize = 0;
|
|
|
|
conf->authcachetype = 0;
|
|
if(strstr((char *) *(argv + 1), "ip")) conf->authcachetype |= 1;
|
|
if(strstr((char *) *(argv + 1), "user")) conf->authcachetype |= 2;
|
|
if(strstr((char *) *(argv + 1), "pass")) conf->authcachetype |= 4;
|
|
if(strstr((char *) *(argv + 1), "limit")) conf->authcachetype |= 8;
|
|
if(strstr((char *) *(argv + 1), "acl")) conf->authcachetype |= 16;
|
|
if(strstr((char *) *(argv + 1), "ext")) conf->authcachetype |= 32;
|
|
if(strstr((char *) *(argv + 1), "dstaddr")) conf->authcachetype |= 64;
|
|
if(strstr((char *) *(argv + 1), "dstport")) conf->authcachetype |= 128;
|
|
if(strstr((char *) *(argv + 1), "dsthost")) conf->authcachetype |= 256;
|
|
if(strstr((char *) *(argv + 1), "dstoper")) conf->authcachetype |= 512;
|
|
if(strstr((char *) *(argv + 1), "srvaddr")) conf->authcachetype |= 1024;
|
|
if(strstr((char *) *(argv + 1), "srvport")) conf->authcachetype |= 2048;
|
|
if(argc > 2) conf->authcachetime = (unsigned) atoi((char *) *(argv + 2));
|
|
if(argc > 3) authcachesize = (unsigned) atoi((char *) *(argv + 3));
|
|
if(!conf->authcachetype) conf->authcachetype = 6;
|
|
if(!conf->authcachetime) conf->authcachetime = 600;
|
|
if(!authcachesize) authcachesize = 65536*4;
|
|
if(auth_table.growlimit != authcachesize && inithashtable(&auth_table, authcachesize < 1024? authcachesize:1024, authcachesize < 1024? authcachesize:1024, authcachesize)){
|
|
fprintf(stderr, "Failed to initialize auth cache\n");
|
|
return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int h_plugin(int argc, unsigned char **argv){
|
|
#ifdef WITH_SSL
|
|
if(argc >= 3 && !strcmp((char *)argv[2], "ssl_plugin")){
|
|
return 0;
|
|
}
|
|
#endif
|
|
#ifdef WITH_PCRE
|
|
if(argc >= 3 && !strcmp((char *)argv[2], "pcre_plugin")){
|
|
return 0;
|
|
}
|
|
#endif
|
|
#ifdef WITH_TRANSPARENT
|
|
if(argc >= 3 && !strcmp((char *)argv[2], "transparent_plugin")){
|
|
return 0;
|
|
}
|
|
#endif
|
|
#ifdef NOPLUGINS
|
|
return 999;
|
|
#else
|
|
#ifdef _WIN32
|
|
HINSTANCE hi;
|
|
FARPROC fp;
|
|
|
|
#ifdef _WINCE
|
|
hi = LoadLibraryW((LPCWSTR)CEToUnicode(argv[1]));
|
|
#else
|
|
hi = LoadLibrary((char *)argv[1]);
|
|
#endif
|
|
if(!hi) {
|
|
fprintf(stderr, "Failed to load %s, code %d\n", argv[1], (int)GetLastError());
|
|
return 1;
|
|
}
|
|
#ifdef _WINCE
|
|
fp = GetProcAddressW(hi, (LPCWSTR)CEToUnicode(argv[2]));
|
|
#else
|
|
fp = GetProcAddress(hi, (char *)argv[2]);
|
|
#endif
|
|
if(!fp) {
|
|
printf("%s not found in %s, code: %d\n", argv[2], argv[1], (int)GetLastError());
|
|
return 2;
|
|
}
|
|
return (*(PLUGINFUNC)fp)(&pluginlink, argc - 2, (char **)argv + 2);
|
|
#else
|
|
void *hi, *fp;
|
|
hi = dlopen((char *)argv[1], RTLD_LAZY);
|
|
if(!hi) {
|
|
fprintf(stderr, "%s", dlerror());
|
|
return 1;
|
|
}
|
|
fp = dlsym(hi, (char *)argv[2]);
|
|
if(!fp) {
|
|
fprintf(stderr, "%s", dlerror());
|
|
return 2;
|
|
}
|
|
return (*(PLUGINFUNC)fp)(&pluginlink, argc - 2, (char **)argv + 2);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
|
|
uid_t strtouid(unsigned char *str){
|
|
uid_t res = 0;
|
|
|
|
if(!isnumber(*(char *)str)){
|
|
struct passwd *pw;
|
|
pw = getpwnam((char *)str);
|
|
if(pw) res = pw->pw_uid;
|
|
}
|
|
else res = atoi((char *)str);
|
|
return res;
|
|
}
|
|
|
|
|
|
static int h_setuid(int argc, unsigned char **argv){
|
|
uid_t res = 0;
|
|
res = strtouid(argv[1]);
|
|
if(!res || setreuid(res,res)) {
|
|
fprintf(stderr, "Unable to set uid %d", res);
|
|
return(1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
gid_t strtogid(unsigned char *str){
|
|
gid_t res = 0;
|
|
|
|
if(!isnumber(*(char *)str)){
|
|
struct group *gr;
|
|
gr = getgrnam((char *)str);
|
|
if(gr) res = gr->gr_gid;
|
|
}
|
|
else res = atoi((char *)str);
|
|
return res;
|
|
}
|
|
|
|
static int h_setgid(int argc, unsigned char **argv){
|
|
gid_t res = 0;
|
|
|
|
res = strtogid(argv[1]);
|
|
if(!res || setregid(res,res)) {
|
|
fprintf(stderr, "Unable to set gid %d", res);
|
|
return(1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* What the C library reads on first use from files a chroot hides, read
|
|
while they can still be found: the time zone, without which every time
|
|
is UTC. Names looked up through the system cannot be helped this way, the
|
|
hosts file and resolv.conf are read again for lookups to come. */
|
|
static void prechroot(void){
|
|
tzset();
|
|
}
|
|
|
|
static int h_chroot(int argc, unsigned char **argv){
|
|
uid_t uid = 0;
|
|
gid_t gid = 0;
|
|
if(argc > 2) {
|
|
uid = strtouid(argv[2]);
|
|
if(!uid){
|
|
fprintf(stderr, "Unable to resolve uid %s", argv[2]);
|
|
return(2);
|
|
}
|
|
}
|
|
if(argc > 3) {
|
|
gid = strtogid(argv[3]);
|
|
if(!gid){
|
|
fprintf(stderr, "Unable to resolve gid %s", argv[3]);
|
|
return(3);
|
|
}
|
|
}
|
|
if(!chrootp){
|
|
char *p;
|
|
|
|
prechroot();
|
|
if(chroot((char *)argv[1])) {
|
|
fprintf(stderr, "Unable to chroot %s", argv[1]);
|
|
return(1);
|
|
}
|
|
p = (char *)argv[1] + strlen((char *)argv[1]) ;
|
|
while (p > (char *)argv[1] && p[-1] == '/'){
|
|
p--;
|
|
*p = 0;
|
|
}
|
|
chrootp = strdup((char *)argv[1]);
|
|
if(!chrootp) return 21;
|
|
}
|
|
if (gid && setregid(gid,gid)) {
|
|
fprintf(stderr, "Unable to set gid %d", (int)gid);
|
|
return(4);
|
|
}
|
|
if (uid && setreuid(uid,uid)) {
|
|
fprintf(stderr, "Unable to set uid %d", (int)uid);
|
|
return(5);
|
|
}
|
|
if(chdir("/")){}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef WITH_SSL
|
|
int h_mitm(int argc, unsigned char **argv);
|
|
int h_nomitm(int argc, unsigned char **argv);
|
|
int h_serv(int argc, unsigned char **argv);
|
|
int h_noserv(int argc, unsigned char **argv);
|
|
int h_cli(int argc, unsigned char **argv);
|
|
int h_nocli(int argc, unsigned char **argv);
|
|
int h_certcache(int argc, unsigned char **argv);
|
|
int h_srvcert(int argc, unsigned char **argv);
|
|
int h_srvkey(int argc, unsigned char **argv);
|
|
int h_clicert(int argc, unsigned char **argv);
|
|
int h_clikey(int argc, unsigned char **argv);
|
|
int h_client_cipher_list(int argc, unsigned char **argv);
|
|
int h_server_cipher_list(int argc, unsigned char **argv);
|
|
int h_client_ciphersuites(int argc, unsigned char **argv);
|
|
int h_server_ciphersuites(int argc, unsigned char **argv);
|
|
int h_server_ca_file(int argc, unsigned char **argv);
|
|
int h_server_ca_key(int argc, unsigned char **argv);
|
|
int h_client_ca_file(int argc, unsigned char **argv);
|
|
int h_client_ca_dir(int argc, unsigned char **argv);
|
|
int h_client_ca_store(int argc, unsigned char **argv);
|
|
int h_client_sni(int argc, unsigned char **argv);
|
|
int h_client_alpn(int argc, unsigned char **argv);
|
|
int h_server_ca_dir(int argc, unsigned char **argv);
|
|
int h_server_ca_store(int argc, unsigned char **argv);
|
|
int h_client_min_proto_version(int argc, unsigned char **argv);
|
|
int h_client_max_proto_version(int argc, unsigned char **argv);
|
|
int h_server_min_proto_version(int argc, unsigned char **argv);
|
|
int h_server_max_proto_version(int argc, unsigned char **argv);
|
|
int h_client_verify(int argc, unsigned char **argv);
|
|
int h_no_client_verify(int argc, unsigned char **argv);
|
|
int h_server_verify(int argc, unsigned char **argv);
|
|
int h_no_server_verify(int argc, unsigned char **argv);
|
|
int h_client_mode(int argc, unsigned char **argv);
|
|
int h_ktls(int argc, unsigned char **argv);
|
|
int h_noktls(int argc, unsigned char **argv);
|
|
#endif
|
|
#ifdef WITH_HTTPSRV
|
|
int h_http_content_type(int argc, unsigned char **argv);
|
|
#endif
|
|
#ifdef WITH_TRANSPARENT
|
|
int h_transparent(int argc, unsigned char **argv);
|
|
int h_notransparent(int argc, unsigned char **argv);
|
|
#endif
|
|
#ifdef WITH_PCRE
|
|
int h_pcre(int argc, unsigned char **argv);
|
|
int h_pcre_rewrite(int argc, unsigned char **argv);
|
|
int h_pcre_extend(int argc, unsigned char **argv);
|
|
int h_pcre_options(int argc, unsigned char **argv);
|
|
#endif
|
|
|
|
struct commands commandhandlers[]={
|
|
{NULL, "", h_noop, 1, 0},
|
|
{NULL, "proxy", h_proxy, 1, 0},
|
|
{NULL, "pop3p", h_proxy, 1, 0},
|
|
{NULL, "imapp", h_proxy, 1, 0},
|
|
{NULL, "ftppr", h_proxy, 1, 0},
|
|
{NULL, "socks", h_proxy, 1, 0},
|
|
{NULL, "tcppm", h_proxy, 4, 0},
|
|
{NULL, "udppm", h_proxy, 4, 0},
|
|
#ifdef WITH_HTTPSRV
|
|
{NULL, "admin", h_proxy, 1, 0},
|
|
#endif
|
|
#ifdef WITH_HTTPSRV
|
|
{NULL, "httpsrv", h_proxy, 1, 0},
|
|
{NULL, "http", h_http, 4, 0},
|
|
{NULL, "http_content_type", h_http_content_type, 3, 3},
|
|
#endif
|
|
{NULL, "dnspr", h_proxy, 1, 0},
|
|
{NULL, "internal", h_internal, 2, 2},
|
|
{NULL, "external", h_external, 2, 2},
|
|
{NULL, "log", h_log, 1, 0},
|
|
{NULL, "service", h_service, 1, 1},
|
|
{NULL, "daemon", h_daemon, 1, 1},
|
|
{NULL, "config", h_config, 2, 2},
|
|
{NULL, "include", h_include, 2, 2},
|
|
{NULL, "archiver", h_archiver, 3, 0},
|
|
{NULL, "counter", h_counter, 2, 4},
|
|
{NULL, "rotate", h_rotate, 2, 2},
|
|
{NULL, "logformat", h_logformat, 2, 2},
|
|
{NULL, "timeouts", h_timeouts, 2, 0},
|
|
{NULL, "auth", h_auth, 2, 0},
|
|
{NULL, "users", h_users, 1, 0},
|
|
{NULL, "maxconn", h_maxconn, 2, 2},
|
|
{NULL, "flush", h_flush, 1, 1},
|
|
{NULL, "nserver", h_nserver, 2, 2},
|
|
{NULL, "fakeresolve", h_fakeresolve, 1, 1},
|
|
{NULL, "nscache", h_nscache, 2, 2},
|
|
{NULL, "nscache6", h_nscache6, 2, 2},
|
|
{NULL, "nsrecord", h_nsrecord, 3, 3},
|
|
{NULL, "dialer", h_dialer, 2, 2},
|
|
{NULL, "system", h_system, 2, 2},
|
|
{NULL, "pidfile", h_pidfile, 2, 2},
|
|
{NULL, "monitor", h_monitor, 2, 2},
|
|
{NULL, "parent", h_parent, 5, 0},
|
|
{NULL, "allow", h_ace, 1, 0},
|
|
{NULL, "deny", h_ace, 1, 0},
|
|
{NULL, "redirect", h_ace, 3, 0},
|
|
{NULL, "bandlimin", h_ace, 2, 0},
|
|
{NULL, "bandlimout", h_ace, 2, 0},
|
|
{NULL, "nobandlimin", h_ace, 1, 0},
|
|
{NULL, "nobandlimout", h_ace, 1, 0},
|
|
{NULL, "countin", h_ace, 4, 0},
|
|
{NULL, "nocountin", h_ace, 1, 0},
|
|
{NULL, "countout", h_ace, 4, 0},
|
|
{NULL, "nocountout", h_ace, 1, 0},
|
|
{NULL, "countall", h_ace, 4, 0},
|
|
{NULL, "nocountall", h_ace, 1, 0},
|
|
{NULL, "connlim", h_ace, 4, 0},
|
|
{NULL, "noconnlim", h_ace, 1, 0},
|
|
{NULL, "plugin", h_plugin, 3, 0},
|
|
{NULL, "logdump", h_logdump, 2, 3},
|
|
{NULL, "filtermaxsize", h_filtermaxsize, 2, 2},
|
|
{NULL, "nolog", h_nolog, 1, 1},
|
|
{NULL, "weight", h_nolog, 2, 2},
|
|
{NULL, "authcache", h_authcache, 2, 4},
|
|
{NULL, "smtpp", h_proxy, 1, 0},
|
|
{NULL, "delimchar",h_delimchar, 2, 2},
|
|
{NULL, "authnserver", h_authnserver, 2, 2},
|
|
{NULL, "stacksize", h_stacksize, 2, 2},
|
|
{NULL, "threadpool", h_threadpool, 2, 3},
|
|
{NULL, "force", h_force, 1, 1},
|
|
{NULL, "noforce", h_noforce, 1, 1},
|
|
{NULL, "parentretries", h_parentretries, 2, 2},
|
|
{NULL, "auto", h_proxy, 1, 0},
|
|
{NULL, "backlog", h_backlog, 2, 2},
|
|
{NULL, "tlspr", h_proxy, 1, 0},
|
|
{NULL, "maxseg", h_maxseg, 2, 2},
|
|
#ifndef NORADIUS
|
|
{NULL, "radius", h_radius, 3, 0},
|
|
#endif
|
|
#ifndef _WIN32
|
|
{NULL, "setuid", h_setuid, 2, 2},
|
|
{NULL, "setgid", h_setgid, 2, 2},
|
|
{NULL, "chroot", h_chroot, 2, 4},
|
|
#endif
|
|
#ifdef WITH_SSL
|
|
{NULL, "ssl_mitm", h_mitm, 1, 1},
|
|
{NULL, "ssl_nomitm", h_nomitm, 1, 1},
|
|
{NULL, "ssl_serv", h_serv, 1, 1},
|
|
{NULL, "ssl_noserv", h_noserv, 1, 1},
|
|
{NULL, "ssl_server_cert", h_srvcert, 1, 2},
|
|
{NULL, "ssl_server_key", h_srvkey, 1, 2},
|
|
{NULL, "ssl_server_ca_file", h_server_ca_file, 1, 2},
|
|
{NULL, "ssl_server_ca_key", h_server_ca_key, 1, 2},
|
|
{NULL, "ssl_client_ca_file", h_client_ca_file, 1, 2},
|
|
{NULL, "ssl_client_ca_dir", h_client_ca_dir, 1, 2},
|
|
{NULL, "ssl_client_ca_store", h_client_ca_store, 1, 2},
|
|
{NULL, "ssl_client_ciphersuites", h_client_ciphersuites, 1, 2},
|
|
{NULL, "ssl_server_ciphersuites", h_server_ciphersuites, 1, 2},
|
|
{NULL, "ssl_client_cipher_list", h_client_cipher_list, 1, 2},
|
|
{NULL, "ssl_server_cipher_list", h_server_cipher_list, 1, 2},
|
|
{NULL, "ssl_client_min_proto_version", h_client_min_proto_version, 1, 2},
|
|
{NULL, "ssl_server_min_proto_version", h_server_min_proto_version, 1, 2},
|
|
{NULL, "ssl_client_max_proto_version", h_client_max_proto_version, 1, 2},
|
|
{NULL, "ssl_server_max_proto_version", h_server_max_proto_version, 1, 2},
|
|
{NULL, "ssl_client_verify", h_client_verify, 1, 1},
|
|
{NULL, "ssl_client_no_verify", h_no_client_verify, 1, 1},
|
|
{NULL, "ssl_cli", h_cli, 1, 1},
|
|
{NULL, "ssl_nocli", h_nocli, 1, 1},
|
|
{NULL, "ssl_client_cert", h_clicert, 1, 2},
|
|
{NULL, "ssl_client_key", h_clikey, 1, 2},
|
|
{NULL, "ssl_server", h_serv, 1, 1},
|
|
{NULL, "ssl_noserver", h_noserv, 1, 1},
|
|
{NULL, "ssl_client", h_cli, 1, 1},
|
|
{NULL, "ssl_noclient", h_nocli, 1, 1},
|
|
{NULL, "ssl_server_verify", h_server_verify, 1, 1},
|
|
{NULL, "ssl_server_no_verify", h_no_server_verify, 1, 1},
|
|
{NULL, "ssl_server_ca_dir", h_server_ca_dir, 1, 2},
|
|
{NULL, "ssl_server_ca_store", h_server_ca_store, 1, 2},
|
|
{NULL, "ssl_client_sni", h_client_sni, 1, 2},
|
|
{NULL, "ssl_client_alpn", h_client_alpn, 1, 0},
|
|
{NULL, "ssl_client_mode", h_client_mode, 1, 2},
|
|
{NULL, "ssl_ktls", h_ktls, 1, 1},
|
|
{NULL, "ssl_noktls", h_noktls, 1, 1},
|
|
{NULL, "ssl_certcache", h_certcache, 2, 2},
|
|
#endif
|
|
#ifdef WITH_TRANSPARENT
|
|
{NULL, "transparent", h_transparent, 1, 2},
|
|
{NULL, "notransparent", h_notransparent, 1, 1},
|
|
#endif
|
|
#ifdef WITH_PCRE
|
|
{NULL, "pcre", h_pcre, 4, 0},
|
|
{NULL, "pcre_rewrite", h_pcre_rewrite, 5, 0},
|
|
{NULL, "pcre_extend", h_pcre_extend, 2, 0},
|
|
{NULL, "pcre_options", h_pcre_options, 2, 0},
|
|
#endif
|
|
{NULL, "", h_noop, 1, 0}
|
|
};
|
|
|
|
void initcommands(void){
|
|
static int initialized = 0;
|
|
unsigned i;
|
|
if(initialized) return;
|
|
initialized = 1;
|
|
for(i = 0; i + 1 < sizeof(commandhandlers)/sizeof(commandhandlers[0]); i++)
|
|
commandhandlers[i].next = commandhandlers + i + 1;
|
|
}
|
|
|
|
int parsestr (unsigned char *str, unsigned char **argm, int nitems, unsigned char ** buff, int *inbuf, int *bufsize){
|
|
#define buf (*buff)
|
|
int argc = 0;
|
|
int space = 1;
|
|
int comment = 0;
|
|
unsigned char * incbegin = 0;
|
|
int fd;
|
|
int res, len;
|
|
unsigned char *str1;
|
|
|
|
for(;;str++){
|
|
if(*str == '\"'){
|
|
str1 = str;
|
|
do {
|
|
*str1 = *(str1 + 1);
|
|
}while(*(str1++));
|
|
if(!comment || *str != '\"'){
|
|
comment = !comment;
|
|
}
|
|
}
|
|
switch(*str){
|
|
case '\0':
|
|
if(comment || incbegin) return -1;
|
|
argm[argc] = 0;
|
|
return argc;
|
|
case '$':
|
|
/* Two dollars stand for one. That is how a literal dollar is
|
|
written where a file to include would otherwise be read, and
|
|
the second one is dropped here as a quote character is. */
|
|
if(str[1] == '$'){
|
|
str1 = str;
|
|
do {
|
|
*str1 = *(str1 + 1);
|
|
}while(*(str1++));
|
|
if(space){
|
|
argm[argc++] = str;
|
|
if(argc >= nitems) return argc;
|
|
space = 0;
|
|
}
|
|
break;
|
|
}
|
|
if(comment){
|
|
if(space){
|
|
argm[argc++] = str;
|
|
if(argc >= nitems) return argc;
|
|
space = 0;
|
|
}
|
|
}
|
|
else if(!included){
|
|
incbegin = str;
|
|
*str = 0;
|
|
}
|
|
break;
|
|
case '\r':
|
|
case '\n':
|
|
case '\t':
|
|
case ' ':
|
|
if(!comment){
|
|
*str = 0;
|
|
space = 1;
|
|
if(incbegin){
|
|
if(argc) argc--;
|
|
if((fd = open((char *)incbegin+1, O_RDONLY)) < 0){
|
|
fprintf(stderr, "Failed to open %s\n", incbegin+1);
|
|
return -1;
|
|
}
|
|
if((*bufsize - *inbuf) <STRINGBUF){
|
|
*bufsize += STRINGBUF;
|
|
if(!(buf = realloc(buf, *bufsize))){
|
|
fprintf(stderr, "Failed to allocate memory for %s\n", incbegin+1);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
}
|
|
len = 0;
|
|
if(argc > 0 && argm[argc]!=(incbegin+1)) {
|
|
len = (int)strlen((char *)argm[argc]);
|
|
memmove(buf+*inbuf, argm[argc], len);
|
|
}
|
|
if((res = read(fd, buf+*inbuf+len, STRINGBUF-(1+len))) <= 0) {
|
|
perror((char *)incbegin+1);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
close(fd);
|
|
buf[*inbuf+res+len] = 0;
|
|
incbegin = buf + *inbuf;
|
|
(*inbuf) += (res + len + 1);
|
|
included++;
|
|
argc+=parsestr(incbegin, argm + argc, nitems - argc, buff, inbuf, bufsize);
|
|
included--;
|
|
incbegin = NULL;
|
|
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
if(space) {
|
|
if(comment && *str == '\"' && str[1] != '\"'){
|
|
str++;
|
|
comment = 0;
|
|
}
|
|
argm[argc++] = str;
|
|
if(argc >= nitems) return argc;
|
|
space = 0;
|
|
}
|
|
}
|
|
}
|
|
#undef buf
|
|
}
|
|
|
|
|
|
int readconfig(FILE * fp){
|
|
unsigned char ** argv = NULL;
|
|
unsigned char * buf = NULL;
|
|
int bufsize = STRINGBUF*2;
|
|
int inbuf = 0;
|
|
int argc;
|
|
struct commands * cm;
|
|
int res = 0;
|
|
|
|
if( !(buf = malloc(bufsize)) || ! (argv = malloc((NPARAMS + 1) * sizeof(unsigned char *))) ) {
|
|
fprintf(stderr, "No memory for configuration");
|
|
if(buf) free(buf);
|
|
return(10);
|
|
}
|
|
for (linenum = 1; fgets((char *)buf, STRINGBUF, fp); linenum++){
|
|
if(!*buf || isspace(*buf) || (*buf) == '#')continue;
|
|
|
|
inbuf = (int)(strlen((char *)buf) + 1);
|
|
argc = parsestr (buf, argv, NPARAMS-1, &buf, &inbuf, &bufsize);
|
|
if(argc < 1) {
|
|
fprintf(stderr, "Parse error line %d\n", linenum);
|
|
res = 11;
|
|
goto DONE;
|
|
}
|
|
argv[argc] = NULL;
|
|
if(!strcmp((char *)argv[0], "end") && argc == 1) {
|
|
break;
|
|
}
|
|
|
|
res = 1;
|
|
for(cm = commandhandlers; cm; cm = cm->next){
|
|
if(!strcmp((char *)argv[0], (char *)cm->command)){
|
|
if(argc < cm->minargs || (cm->maxargs && argc > cm->maxargs)){
|
|
fprintf(stderr, "Command: '%s' wrong number of arguments , line %d\n", argv[0], linenum);
|
|
res = linenum;
|
|
goto DONE;
|
|
}
|
|
res = (*cm->handler)(argc, argv);
|
|
if(res > 0){
|
|
fprintf(stderr, "Command: '%s' failed with code %d, line %d\n", argv[0], res, linenum);
|
|
res = linenum;
|
|
goto DONE;
|
|
}
|
|
if(!res) break;
|
|
}
|
|
}
|
|
if(res != 1)continue;
|
|
fprintf(stderr, "Unknown command: '%s' line %d\n", argv[0], linenum);
|
|
res = linenum;
|
|
goto DONE;
|
|
}
|
|
res = 0;
|
|
DONE:
|
|
free(buf);
|
|
free(argv);
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
void freepwl(struct passwords *pwl){
|
|
for(; pwl; pwl = (struct passwords *)itfree(pwl, pwl->next)){
|
|
if(pwl->user)free(pwl->user);
|
|
if(pwl->password)free(pwl->password);
|
|
}
|
|
}
|
|
|
|
|
|
/* Releases what a configuration holds, once nothing uses it. Nothing global
|
|
is touched: a configuration may be released while another is in force. */
|
|
void freeconf(struct extparam *confp){
|
|
struct bandlim * bl;
|
|
struct bandlim * blout;
|
|
struct connlim * cl;
|
|
struct trafcount * tc;
|
|
struct ace *acl;
|
|
struct filemon *fm;
|
|
int counterd, archiverc;
|
|
unsigned char **archiver;
|
|
unsigned char * logformat;
|
|
int i;
|
|
|
|
_3proxy_mutex_lock(&tc_mutex);
|
|
confp->trafcountfunc = NULL;
|
|
tc = confp->trafcounter;
|
|
confp->trafcounter = NULL;
|
|
counterd = confp->counterd;
|
|
confp->counterd = -1;
|
|
confp->countertype = NONE;
|
|
_3proxy_mutex_unlock(&tc_mutex);
|
|
|
|
_3proxy_mutex_lock(&bandlim_mutex);
|
|
bl = confp->bandlimiter;
|
|
blout = confp->bandlimiterout;
|
|
confp->bandlimiter = NULL;
|
|
confp->bandlimiterout = NULL;
|
|
confp->bandlimfunc = NULL;
|
|
confp->bandlimver++;
|
|
_3proxy_mutex_unlock(&bandlim_mutex);
|
|
_3proxy_mutex_lock(&connlim_mutex);
|
|
cl = confp->connlimiter;
|
|
confp->connlimiter = NULL;
|
|
_3proxy_mutex_unlock(&connlim_mutex);
|
|
|
|
if(confp->pwl_table.ihashtable) destroyhashtable(&confp->pwl_table);
|
|
confp->logfunc = lognone;
|
|
logformat = confp->logformat;
|
|
confp->logformat = NULL;
|
|
archiverc = confp->archiverc;
|
|
confp->archiverc = 0;
|
|
archiver = confp->archiver;
|
|
confp->archiver = NULL;
|
|
fm = confp->fmon;
|
|
confp->fmon = NULL;
|
|
acl = confp->acl;
|
|
confp->acl = NULL;
|
|
|
|
if(tc)dumpcounters(tc,counterd);
|
|
for(; tc; tc = (struct trafcount *) itfree(tc, tc->next)){
|
|
if(tc->comment)free(tc->comment);
|
|
freeacl(tc->ace);
|
|
}
|
|
freeacl(acl);
|
|
for(; bl; bl = (struct bandlim *) itfree(bl, bl->next)) freeacl(bl->ace);
|
|
for(; blout; blout = (struct bandlim *) itfree(blout, blout->next))freeacl(blout->ace);
|
|
for(; cl; cl = (struct connlim *) itfree(cl, cl->next)) freeacl(cl->ace);
|
|
|
|
if(counterd != -1) {
|
|
close(counterd);
|
|
}
|
|
for(; fm; fm = (struct filemon *)itfree(fm, fm->next)){
|
|
if(fm->path) free(fm->path);
|
|
}
|
|
if(logformat) {
|
|
free(logformat);
|
|
}
|
|
if(archiver) {
|
|
for(i = 0; i < archiverc; i++) free(archiver[i]);
|
|
free(archiver);
|
|
}
|
|
if(confp->authfuncs) freeauth(confp->authfuncs);
|
|
confp->authfuncs = NULL;
|
|
freepwl(confp->pwl);
|
|
confp->pwl = NULL;
|
|
#ifdef WITH_HTTPSRV
|
|
if(confp->httprules) freehttprules(confp->httprules);
|
|
confp->httprules = NULL;
|
|
#endif
|
|
if(confp->logname) free(confp->logname);
|
|
confp->logname = NULL;
|
|
if(confp->logtarget) free(confp->logtarget);
|
|
confp->logtarget = NULL;
|
|
if(confp->counterfile) free(confp->counterfile);
|
|
confp->counterfile = NULL;
|
|
if(confp->conffile) free(confp->conffile);
|
|
confp->conffile = NULL;
|
|
}
|
|
|
|
/* What every reading of the file starts from, before its first command. */
|
|
void confbegin(void){
|
|
char * args[] = {"auth", "iponly", NULL};
|
|
|
|
h_auth(2, (unsigned char **)args);
|
|
}
|
|
|
|
/* The configuration in force, held until confput(): for what uses one
|
|
without a service of its own, the resolver for one. */
|
|
struct extparam * confget(void){
|
|
struct extparam *c;
|
|
|
|
_3proxy_mutex_lock(&confref_mutex);
|
|
c = runconf;
|
|
c->refs++;
|
|
_3proxy_mutex_unlock(&confref_mutex);
|
|
return c;
|
|
}
|
|
|
|
void confhold(struct extparam *c){
|
|
_3proxy_mutex_lock(&confref_mutex);
|
|
c->refs++;
|
|
_3proxy_mutex_unlock(&confref_mutex);
|
|
}
|
|
|
|
/* The last to let go of a configuration no longer in force releases it. */
|
|
void confput(struct extparam *c){
|
|
int gone;
|
|
|
|
_3proxy_mutex_lock(&confref_mutex);
|
|
gone = (--c->refs <= 0 && c != runconf);
|
|
_3proxy_mutex_unlock(&confref_mutex);
|
|
if(gone){
|
|
freeconf(c);
|
|
if(c != &confboot) free(c);
|
|
}
|
|
}
|
|
|
|
/* During a reload the services are prepared as their lines are read, but
|
|
started only once the whole file has been read without an error. */
|
|
struct deferred {
|
|
void *service;
|
|
int linenum;
|
|
struct deferred *next;
|
|
};
|
|
static struct deferred *deferred = NULL;
|
|
static int deferring = 0;
|
|
|
|
/* Keeps a prepared service for later if a reload is reading the file: 1,
|
|
or 0 if none is, and the service is to be started now, -1 for want of
|
|
memory. The services start in the order of their lines. */
|
|
static int deferred_add(void *service){
|
|
struct deferred *d, **dp;
|
|
|
|
if(!deferring) return 0;
|
|
if(!(d = malloc(sizeof(*d)))) return -1;
|
|
d->service = service;
|
|
d->linenum = linenum;
|
|
d->next = NULL;
|
|
for(dp = &deferred; *dp; dp = &(*dp)->next);
|
|
*dp = d;
|
|
return 1;
|
|
}
|
|
|
|
int reload (void){
|
|
FILE *fp;
|
|
int error = -2;
|
|
struct extparam *old = conf, *new;
|
|
struct deferred *d;
|
|
|
|
if(!(new = malloc(sizeof(*new)))) return -1;
|
|
*new = confdefaults;
|
|
/* what belongs to the process rather than to one reading of the file:
|
|
the open log, which a log command reopens, and the filters, a list
|
|
of static entries the TLS, PCRE and transparent code link in once */
|
|
new->conffile = old->conffile;
|
|
new->stringtable = old->stringtable;
|
|
new->demon = old->demon;
|
|
new->stdlog = old->stdlog;
|
|
new->filters = old->filters;
|
|
new->version = old->version + 1;
|
|
new->refs = 1; /* this reload's, until it is done with it */
|
|
|
|
_3proxy_mutex_lock(&config_mutex);
|
|
conf = new;
|
|
pluginlink.conf = new;
|
|
#ifdef WITH_SSL
|
|
ssl_install();
|
|
#endif
|
|
#ifdef WITH_PCRE
|
|
pcre_install();
|
|
#endif
|
|
#ifdef WITH_TRANSPARENT
|
|
transparent_install();
|
|
#endif
|
|
confbegin();
|
|
deferring = 1;
|
|
fp = confopen();
|
|
if(fp){
|
|
error = readconfig(fp);
|
|
fclose(fp);
|
|
}
|
|
deferring = 0;
|
|
|
|
if(error){
|
|
/* the old configuration stays in force, its services running:
|
|
what was prepared for the new one is dropped */
|
|
while((d = deferred)){
|
|
deferred = d->next;
|
|
maindrop(d->service);
|
|
free(d);
|
|
}
|
|
old->conffile = new->conffile;
|
|
old->stdlog = new->stdlog;
|
|
old->filters = new->filters;
|
|
new->conffile = NULL;
|
|
new->stdlog = NULL;
|
|
new->filters = NULL;
|
|
new->stringtable = NULL;
|
|
conf = old;
|
|
pluginlink.conf = old;
|
|
_3proxy_mutex_unlock(&config_mutex);
|
|
confput(new);
|
|
return error;
|
|
}
|
|
|
|
old->conffile = NULL;
|
|
old->stdlog = NULL;
|
|
old->filters = NULL;
|
|
old->stringtable = NULL;
|
|
confhold(old);
|
|
_3proxy_mutex_lock(&confref_mutex);
|
|
runconf = new;
|
|
_3proxy_mutex_unlock(&confref_mutex);
|
|
resolvfunc = new->resolvfunc;
|
|
/* the old services stop accepting, the new ones start */
|
|
old->paused++;
|
|
while((d = deferred)){
|
|
deferred = d->next;
|
|
linenum = d->linenum;
|
|
startservice(d->service);
|
|
free(d);
|
|
}
|
|
_3proxy_mutex_unlock(&config_mutex);
|
|
confput(new);
|
|
confput(old);
|
|
return 0;
|
|
}
|