mirror of
https://github.com/3proxy/3proxy.git
synced 2026-08-26 09:55:48 +08:00
implement small http server for admin and testing
This commit is contained in:
parent
a3b40e6176
commit
3526759e59
@ -56,6 +56,7 @@ option(3PROXY_USE_POLL "Use poll() instead of select() (Unix only)" ON)
|
|||||||
option(3PROXY_USE_WSAPOLL "Use WSAPoll instead of select() (Windows only)" ON)
|
option(3PROXY_USE_WSAPOLL "Use WSAPoll instead of select() (Windows only)" ON)
|
||||||
option(3PROXY_USE_NETFILTER "Enable Linux netfilter support (Linux only)" ON)
|
option(3PROXY_USE_NETFILTER "Enable Linux netfilter support (Linux only)" ON)
|
||||||
option(3PROXY_USE_UNIX_SOCKETS "Enable Unix domain socket support (Unix only)" ON)
|
option(3PROXY_USE_UNIX_SOCKETS "Enable Unix domain socket support (Unix only)" ON)
|
||||||
|
option(3PROXY_USE_HTTPSRV "Build the HTTP server and the admin interface on top of it" ON)
|
||||||
|
|
||||||
if(NOT WIN32 AND NOT APPLE)
|
if(NOT WIN32 AND NOT APPLE)
|
||||||
option(3PROXY_STATIC_LINK "Statically link libraries using -Wl,-Bstatic (Linux/Unix only)" OFF)
|
option(3PROXY_STATIC_LINK "Statically link libraries using -Wl,-Bstatic (Linux/Unix only)" OFF)
|
||||||
@ -185,7 +186,11 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
|||||||
add_compile_definitions(WITH_NETFILTER)
|
add_compile_definitions(WITH_NETFILTER)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(3PROXY_USE_UNIX_SOCKETS)
|
if(3PROXY_USE_HTTPSRV)
|
||||||
|
add_compile_definitions(WITH_HTTPSRV)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(3PROXY_USE_UNIX_SOCKETS)
|
||||||
add_compile_definitions(WITH_UN)
|
add_compile_definitions(WITH_UN)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -403,6 +408,7 @@ add_library(srv_modules OBJECT
|
|||||||
src/auto.c
|
src/auto.c
|
||||||
src/socks.c
|
src/socks.c
|
||||||
src/webadmin.c
|
src/webadmin.c
|
||||||
|
src/httpsrv.c
|
||||||
src/dnspr.c
|
src/dnspr.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -24,6 +24,11 @@ LDFLAGS += $(EXTRA_LDFLAGS)
|
|||||||
# -lpthreads may be reuiured on some platforms instead of -pthreads
|
# -lpthreads may be reuiured on some platforms instead of -pthreads
|
||||||
# -ldl or -lld may be required for some platforms
|
# -ldl or -lld may be required for some platforms
|
||||||
DCFLAGS ?= -fPIC
|
DCFLAGS ?= -fPIC
|
||||||
|
HTTPSRV ?= true
|
||||||
|
ifeq ($(HTTPSRV),true)
|
||||||
|
CFLAGS += -DWITH_HTTPSRV
|
||||||
|
HTTPSRV_OBJS = srvhttpsrv$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS)
|
||||||
|
endif
|
||||||
DLFLAGS ?= -shared
|
DLFLAGS ?= -shared
|
||||||
DLSUFFICS = .so
|
DLSUFFICS = .so
|
||||||
LIBS ?=
|
LIBS ?=
|
||||||
|
|||||||
@ -25,6 +25,13 @@ LDFLAGS += -fno-strict-aliasing -pthread
|
|||||||
# makefile, including the += above and the STATIC/LIBSTATIC handling below.
|
# makefile, including the += above and the STATIC/LIBSTATIC handling below.
|
||||||
CFLAGS += $(EXTRA_CFLAGS)
|
CFLAGS += $(EXTRA_CFLAGS)
|
||||||
LDFLAGS += $(EXTRA_LDFLAGS)
|
LDFLAGS += $(EXTRA_LDFLAGS)
|
||||||
|
# The HTTP server serves the endpoints declared by http lines. The admin
|
||||||
|
# interface is built on top of it, so turning it off removes both.
|
||||||
|
HTTPSRV ?= true
|
||||||
|
ifeq ($(HTTPSRV),true)
|
||||||
|
CFLAGS += -DWITH_HTTPSRV
|
||||||
|
HTTPSRV_OBJS = srvhttpsrv$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS)
|
||||||
|
endif
|
||||||
DLFLAGS ?= -shared
|
DLFLAGS ?= -shared
|
||||||
DLSUFFICS = .ld.so
|
DLSUFFICS = .ld.so
|
||||||
# -lpthreads may be reuqired on some platforms instead of -pthreads
|
# -lpthreads may be reuqired on some platforms instead of -pthreads
|
||||||
|
|||||||
@ -14,6 +14,11 @@ COUT = -o ./
|
|||||||
LN = $(CC)
|
LN = $(CC)
|
||||||
LDFLAGS = -xO3
|
LDFLAGS = -xO3
|
||||||
DCFLAGS = -fPIC
|
DCFLAGS = -fPIC
|
||||||
|
HTTPSRV ?= true
|
||||||
|
ifeq ($(HTTPSRV),true)
|
||||||
|
CFLAGS += -DWITH_HTTPSRV
|
||||||
|
HTTPSRV_OBJS = srvhttpsrv$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS)
|
||||||
|
endif
|
||||||
DLFLAGS = -shared
|
DLFLAGS = -shared
|
||||||
DLSUFFICS = .ld.so
|
DLSUFFICS = .ld.so
|
||||||
LIBS = -lpthread -lsocket -lnsl -lresolv -ldl
|
LIBS = -lpthread -lsocket -lnsl -lresolv -ldl
|
||||||
|
|||||||
@ -18,7 +18,7 @@ SSL_LIBS = wolfssl.lib
|
|||||||
SSL_DEFS = /D "WITH_SSL"
|
SSL_DEFS = /D "WITH_SSL"
|
||||||
SSL_LIBS = libcrypto.lib libssl.lib
|
SSL_LIBS = libcrypto.lib libssl.lib
|
||||||
!ENDIF
|
!ENDIF
|
||||||
CFLAGS = /nologo /MT /W3 /Ox /GS /EHs- /GA /GF /D "MSVC" /D "WITH_WSAPOLL" /D "NDEBUG" /D "WIN32" $(SSL_DEFS) /D "WITH_PCRE" /D "WITH_ODBC" /D "_CONSOLE" /D "_MBCS" /D "_WIN32" /Fp"proxy.pch" /FD /c $(BUILDDATE) $(VERSION)
|
CFLAGS = /D "WITH_HTTPSRV" /nologo /MT /W3 /Ox /GS /EHs- /GA /GF /D "MSVC" /D "WITH_WSAPOLL" /D "NDEBUG" /D "WIN32" $(SSL_DEFS) /D "WITH_PCRE" /D "WITH_ODBC" /D "_CONSOLE" /D "_MBCS" /D "_WIN32" /Fp"proxy.pch" /FD /c $(BUILDDATE) $(VERSION)
|
||||||
COUT = /Fo
|
COUT = /Fo
|
||||||
LN = link
|
LN = link
|
||||||
LDFLAGS = /nologo /subsystem:console /incremental:no
|
LDFLAGS = /nologo /subsystem:console /incremental:no
|
||||||
@ -40,6 +40,7 @@ MAKEFILE = Makefile.msvc
|
|||||||
PLUGINS = utf8tocp1251 WindowsAuthentication TrafficPlugin StringsPlugin FilePlugin
|
PLUGINS = utf8tocp1251 WindowsAuthentication TrafficPlugin StringsPlugin FilePlugin
|
||||||
SSL_OBJS = ssllib$(OBJSUFFICS) ssl$(OBJSUFFICS)
|
SSL_OBJS = ssllib$(OBJSUFFICS) ssl$(OBJSUFFICS)
|
||||||
PCRE_OBJS = pcre$(OBJSUFFICS)
|
PCRE_OBJS = pcre$(OBJSUFFICS)
|
||||||
|
HTTPSRV_OBJS = srvhttpsrv$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS)
|
||||||
VERFILE = 3proxy.res $(VERFILE)
|
VERFILE = 3proxy.res $(VERFILE)
|
||||||
VERSIONDEP = 3proxy.res $(VERSIONDEP)
|
VERSIONDEP = 3proxy.res $(VERSIONDEP)
|
||||||
AFTERCLEAN = if exist src\*.res (del src\*.res) && if exist src\*.err (del src\*.err)
|
AFTERCLEAN = if exist src\*.res (del src\*.res) && if exist src\*.err (del src\*.err)
|
||||||
|
|||||||
@ -26,6 +26,11 @@ LDFLAGS += $(EXTRA_LDFLAGS)
|
|||||||
# -lpthreads may be reuqired on some platforms instead of -pthreads
|
# -lpthreads may be reuqired on some platforms instead of -pthreads
|
||||||
# -ldl or -lld may be required for some platforms
|
# -ldl or -lld may be required for some platforms
|
||||||
DCFLAGS ?= -fPIC
|
DCFLAGS ?= -fPIC
|
||||||
|
HTTPSRV ?= true
|
||||||
|
ifeq ($(HTTPSRV),true)
|
||||||
|
CFLAGS += -DWITH_HTTPSRV
|
||||||
|
HTTPSRV_OBJS = srvhttpsrv$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS)
|
||||||
|
endif
|
||||||
DLFLAGS ?= -shared
|
DLFLAGS ?= -shared
|
||||||
DLSUFFICS ?= .ld.so
|
DLSUFFICS ?= .ld.so
|
||||||
LIBS ?=
|
LIBS ?=
|
||||||
|
|||||||
@ -8,7 +8,7 @@ BUILDDIR = ../bin/
|
|||||||
PREFIX = 3proxy_
|
PREFIX = 3proxy_
|
||||||
CRYPT_PREFIX = 3proxy_
|
CRYPT_PREFIX = 3proxy_
|
||||||
CC = cl
|
CC = cl
|
||||||
CFLAGS = /nologo /Ox /MT /D "NOIPV6" /D "NO_UN" /D "NODEBUG" /D "NORADIUS" /D"WATCOM" /D "MSVC" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "_WIN32" /D "PRId64=\"I64d\"" /D "PRIu64=\"I64u\"" /D "SCNu64=\"I64u\"" /D "SCNx64=\"I64x\"" /D "SCNd64=\"I64d\"" /D "PRIx64=\"I64x\"" /c $(VERSION) $(BUILDDATE)
|
CFLAGS = /D "WITH_HTTPSRV" /nologo /Ox /MT /D "NOIPV6" /D "NO_UN" /D "NODEBUG" /D "NORADIUS" /D"WATCOM" /D "MSVC" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "_WIN32" /D "PRId64=\"I64d\"" /D "PRIu64=\"I64u\"" /D "SCNu64=\"I64u\"" /D "SCNx64=\"I64x\"" /D "SCNd64=\"I64d\"" /D "PRIx64=\"I64x\"" /c $(VERSION) $(BUILDDATE)
|
||||||
COUT = /Fo
|
COUT = /Fo
|
||||||
LN = link
|
LN = link
|
||||||
LDFLAGS = /nologo /subsystem:console /incremental:no
|
LDFLAGS = /nologo /subsystem:console /incremental:no
|
||||||
@ -21,6 +21,7 @@ LIBEXT = .lib
|
|||||||
LNOUT = /out:
|
LNOUT = /out:
|
||||||
EXESUFFICS = .exe
|
EXESUFFICS = .exe
|
||||||
OBJSUFFICS = .obj
|
OBJSUFFICS = .obj
|
||||||
|
HTTPSRV_OBJS = srvhttpsrv$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS)
|
||||||
DEFINEOPTION = /D
|
DEFINEOPTION = /D
|
||||||
COMPFILES = *.pch *.idb *.err
|
COMPFILES = *.pch *.idb *.err
|
||||||
REMOVECOMMAND = del 2>NUL >NUL
|
REMOVECOMMAND = del 2>NUL >NUL
|
||||||
|
|||||||
@ -20,6 +20,11 @@ LDFLAGS += -fno-strict-aliasing -mthreads
|
|||||||
# makefile, including the += above and the STATIC/LIBSTATIC handling below.
|
# makefile, including the += above and the STATIC/LIBSTATIC handling below.
|
||||||
CFLAGS += $(EXTRA_CFLAGS)
|
CFLAGS += $(EXTRA_CFLAGS)
|
||||||
LDFLAGS += $(EXTRA_LDFLAGS)
|
LDFLAGS += $(EXTRA_LDFLAGS)
|
||||||
|
HTTPSRV ?= true
|
||||||
|
ifeq ($(HTTPSRV),true)
|
||||||
|
CFLAGS += -DWITH_HTTPSRV
|
||||||
|
HTTPSRV_OBJS = srvhttpsrv$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS)
|
||||||
|
endif
|
||||||
DLFLAGS ?= -shared
|
DLFLAGS ?= -shared
|
||||||
DLSUFFICS = .dll
|
DLSUFFICS = .dll
|
||||||
LIBS += -lws2_32 -lodbc32 -ladvapi32 -luser32 -lbcrypt
|
LIBS += -lws2_32 -lodbc32 -ladvapi32 -luser32 -lbcrypt
|
||||||
|
|||||||
@ -282,16 +282,7 @@ proxy on a client with FTP proxy support. Username format is one of
|
|||||||
.BR config
|
.BR config
|
||||||
\fI<path>\fR
|
\fI<path>\fR
|
||||||
.br
|
.br
|
||||||
Path to configuration file to use on 3proxy restart or to save configuration.
|
Path to configuration file to use on 3proxy restart.
|
||||||
|
|
||||||
.br
|
|
||||||
.B writable
|
|
||||||
.br
|
|
||||||
ReOpens configuration file for write access via Web interface,
|
|
||||||
and rereads it. Usually should be first command on config file
|
|
||||||
but in combination with config
|
|
||||||
it can be used anywhere to open
|
|
||||||
alternate config file. Think twice before using it.
|
|
||||||
|
|
||||||
.br
|
.br
|
||||||
.B end
|
.B end
|
||||||
|
|||||||
1
rus.3ps
1
rus.3ps
@ -24,7 +24,6 @@ Content-type: text/html; charset=utf-8\n
|
|||||||
<A HREF='/C'>Счетчики</A><br><br>\n
|
<A HREF='/C'>Счетчики</A><br><br>\n
|
||||||
<A HREF='/R'>Перезагрузка конфигурации сервера</A><br><br>\n
|
<A HREF='/R'>Перезагрузка конфигурации сервера</A><br><br>\n
|
||||||
<A HREF='/S'>Запущенные сервисы</A><br><br>\n
|
<A HREF='/S'>Запущенные сервисы</A><br><br>\n
|
||||||
<A HREF='/F'>Настройка сервера</A>\n
|
|
||||||
</td><td>
|
</td><td>
|
||||||
<h2>%s %s Конфигурация</h2>
|
<h2>%s %s Конфигурация</h2>
|
||||||
[end]
|
[end]
|
||||||
|
|||||||
@ -28,7 +28,6 @@ void pcre_install(void);
|
|||||||
|
|
||||||
FILE * confopen();
|
FILE * confopen();
|
||||||
extern unsigned char *strings[];
|
extern unsigned char *strings[];
|
||||||
extern FILE *writable;
|
|
||||||
extern struct counter_header cheader;
|
extern struct counter_header cheader;
|
||||||
extern struct counter_record crecord;
|
extern struct counter_record crecord;
|
||||||
|
|
||||||
@ -537,7 +536,7 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
|
|||||||
conf.version++;
|
conf.version++;
|
||||||
|
|
||||||
if(res) RETURN(res);
|
if(res) RETURN(res);
|
||||||
if(!writable){fclose(fp); fp = NULL;}
|
fclose(fp); fp = NULL;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
|||||||
@ -116,6 +116,9 @@ srvsocks$(OBJSUFFICS): socks.c proxy.h structures.h
|
|||||||
srvwebadmin$(OBJSUFFICS): webadmin.c proxy.h structures.h
|
srvwebadmin$(OBJSUFFICS): webadmin.c proxy.h structures.h
|
||||||
$(CC) $(COUT)srvwebadmin$(OBJSUFFICS) $(CFLAGS) webadmin.c
|
$(CC) $(COUT)srvwebadmin$(OBJSUFFICS) $(CFLAGS) webadmin.c
|
||||||
|
|
||||||
|
srvhttpsrv$(OBJSUFFICS): httpsrv.c proxy.h structures.h
|
||||||
|
$(CC) $(COUT)srvhttpsrv$(OBJSUFFICS) $(CFLAGS) httpsrv.c
|
||||||
|
|
||||||
srvudppm$(OBJSUFFICS): udppm.c proxy.h structures.h
|
srvudppm$(OBJSUFFICS): udppm.c proxy.h structures.h
|
||||||
$(CC) $(COUT)srvudppm$(OBJSUFFICS) $(CFLAGS) udppm.c
|
$(CC) $(COUT)srvudppm$(OBJSUFFICS) $(CFLAGS) udppm.c
|
||||||
|
|
||||||
@ -188,6 +191,6 @@ ssl$(OBJSUFFICS): ssl.c structures.h proxy.h ssl.h
|
|||||||
pcre$(OBJSUFFICS): pcre.c structures.h
|
pcre$(OBJSUFFICS): pcre.c structures.h
|
||||||
$(CC) $(COUT)pcre$(OBJSUFFICS) $(CFLAGS) $(DEFINEOPTION)WITH_PCRE pcre.c
|
$(CC) $(COUT)pcre$(OBJSUFFICS) $(CFLAGS) $(DEFINEOPTION)WITH_PCRE pcre.c
|
||||||
|
|
||||||
$(BUILDDIR)3proxy$(EXESUFFICS): 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) log$(OBJSUFFICS) datatypes$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(COMPATLIBS) $(VERSIONDEP)
|
$(BUILDDIR)3proxy$(EXESUFFICS): 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) log$(OBJSUFFICS) datatypes$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) $(HTTPSRV_OBJS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(COMPATLIBS) $(VERSIONDEP)
|
||||||
$(LN) $(LNOUT)$(BUILDDIR)3proxy$(EXESUFFICS) $(LDFLAGS) $(VERFILE) 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) datatypes$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(COMPATLIBS) $(LIBS) $(PCRE_LIBS)
|
$(LN) $(LNOUT)$(BUILDDIR)3proxy$(EXESUFFICS) $(LDFLAGS) $(VERFILE) 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) datatypes$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) $(HTTPSRV_OBJS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(COMPATLIBS) $(LIBS) $(PCRE_LIBS)
|
||||||
|
|
||||||
|
|||||||
31
src/acl.c
31
src/acl.c
@ -62,36 +62,7 @@ int ACLmatches(struct ace* acentry, struct clientparam * param){
|
|||||||
}
|
}
|
||||||
while(i > 5 && param->hostname[i-1] == '.') param->hostname[i-1] = 0;
|
while(i > 5 && param->hostname[i-1] == '.') param->hostname[i-1] = 0;
|
||||||
for(hstentry = acentry->dstnames; hstentry; hstentry = hstentry->next){
|
for(hstentry = acentry->dstnames; hstentry; hstentry = hstentry->next){
|
||||||
int lname, lhost;
|
if(patternmatch(hstentry, param->hostname)) match = 1;
|
||||||
switch(hstentry->matchtype){
|
|
||||||
case 0:
|
|
||||||
#ifndef _WIN32
|
|
||||||
if(strcasestr((char *)param->hostname, (char *)hstentry->name)) match = 1;
|
|
||||||
#else
|
|
||||||
if(strstr((char *)param->hostname, (char *)hstentry->name)) match = 1;
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
if(!strncasecmp((char *)param->hostname, (char *)hstentry->name, strlen((char *)hstentry->name)))
|
|
||||||
match = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
lname = strlen((char *)hstentry->name);
|
|
||||||
lhost = strlen((char *)param->hostname);
|
|
||||||
if(lhost > lname){
|
|
||||||
if(!strncasecmp((char *)param->hostname + (lhost - lname),
|
|
||||||
(char *)hstentry->name,
|
|
||||||
lname))
|
|
||||||
match = 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if(!strcasecmp((char *)param->hostname, (char *)hstentry->name)) match = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(match) break;
|
if(match) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,7 +18,13 @@ int alwaysauth(struct clientparam * param){
|
|||||||
|
|
||||||
|
|
||||||
if(conf.connlimiter && !param->connlim && startconnlims(param)) return 10;
|
if(conf.connlimiter && !param->connlim && startconnlims(param)) return 10;
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
/* The http server answers the request itself, so authorization must not
|
||||||
|
try to reach a destination that does not exist. */
|
||||||
|
res = (param->srv->service == S_HTTPSRV)? 0 : doconnect(param);
|
||||||
|
#else
|
||||||
res = doconnect(param);
|
res = doconnect(param);
|
||||||
|
#endif
|
||||||
if(!res){
|
if(!res){
|
||||||
if(conf.bandlimfunc && (conf.bandlimiter||conf.bandlimiterout)){
|
if(conf.bandlimfunc && (conf.bandlimiter||conf.bandlimiterout)){
|
||||||
_3proxy_mutex_lock(&bandlim_mutex);
|
_3proxy_mutex_lock(&bandlim_mutex);
|
||||||
|
|||||||
96
src/common.c
96
src/common.c
@ -807,6 +807,102 @@ int bindwithrange(struct clientparam *param, SOCKET sock, PROXYSOCKADDRTYPE *sa,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Host lists in access rules have always accepted name, name*, *name and
|
||||||
|
*name*, with the leading and trailing star recorded as a match type rather
|
||||||
|
than kept in the string. The parser and the comparison are here so that
|
||||||
|
anything else matching a name against a pattern - the http command, and
|
||||||
|
whatever replaces the star with a regular expression later - behaves the same
|
||||||
|
way and gains the same syntax at the same time.
|
||||||
|
*/
|
||||||
|
int parsepattern(struct hostname *h, unsigned char *arg)
|
||||||
|
{
|
||||||
|
int arglen;
|
||||||
|
unsigned char *pattern;
|
||||||
|
|
||||||
|
arglen = (int)strlen((char *)arg);
|
||||||
|
h->matchtype = 3;
|
||||||
|
pattern = arg;
|
||||||
|
|
||||||
|
if(arglen && pattern[arglen-1] == '*'){
|
||||||
|
arglen--;
|
||||||
|
pattern[arglen] = 0;
|
||||||
|
h->matchtype ^= MATCHEND;
|
||||||
|
}
|
||||||
|
if(arglen && pattern[0] == '*'){
|
||||||
|
pattern++;
|
||||||
|
arglen--;
|
||||||
|
h->matchtype ^= MATCHBEGIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
h->name = (unsigned char *)strdup((char *)pattern);
|
||||||
|
return h->name? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Matches str against a pattern and reports the part a star stood for. Where a
|
||||||
|
pattern has a star at both ends the trailing one is reported, since that is
|
||||||
|
the part following the text that was matched. An exact pattern leaves an
|
||||||
|
empty span. */
|
||||||
|
int patternmatchpos(const struct hostname *h, const unsigned char *str, int *start, int *len)
|
||||||
|
{
|
||||||
|
int lname, lstr, pos = 0, match = 0;
|
||||||
|
char *found;
|
||||||
|
|
||||||
|
if(!h->name || !str) return 0;
|
||||||
|
|
||||||
|
lname = (int)strlen((char *)h->name);
|
||||||
|
lstr = (int)strlen((char *)str);
|
||||||
|
|
||||||
|
switch(h->matchtype){
|
||||||
|
case 0:
|
||||||
|
#ifndef _WIN32
|
||||||
|
found = strcasestr((char *)str, (char *)h->name);
|
||||||
|
#else
|
||||||
|
found = strstr((char *)str, (char *)h->name);
|
||||||
|
#endif
|
||||||
|
if(found){
|
||||||
|
match = 1;
|
||||||
|
pos = (int)(found - (char *)str) + lname;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
if(!strncasecmp((char *)str, (char *)h->name, lname)){
|
||||||
|
match = 1;
|
||||||
|
pos = lname;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
if(lstr >= lname &&
|
||||||
|
!strncasecmp((char *)str + (lstr - lname), (char *)h->name, lname)){
|
||||||
|
match = 1;
|
||||||
|
pos = 0;
|
||||||
|
if(start) *start = 0;
|
||||||
|
if(len) *len = lstr - lname;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if(!strcasecmp((char *)str, (char *)h->name)){
|
||||||
|
match = 1;
|
||||||
|
pos = lstr;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!match) return 0;
|
||||||
|
|
||||||
|
if(start) *start = pos;
|
||||||
|
if(len) *len = lstr - pos;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int patternmatch(const struct hostname *h, const unsigned char *str)
|
||||||
|
{
|
||||||
|
return patternmatchpos(h, str, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
int scanaddr(const unsigned char *s, uint32_t * ip, uint32_t * mask) {
|
int scanaddr(const unsigned char *s, uint32_t * ip, uint32_t * mask) {
|
||||||
unsigned d1, d2, d3, d4, m;
|
unsigned d1, d2, d3, d4, m;
|
||||||
int res;
|
int res;
|
||||||
|
|||||||
155
src/conf.c
155
src/conf.c
@ -7,6 +7,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "proxy.h"
|
#include "proxy.h"
|
||||||
|
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
static int addhttprule(char *host, char *url, char *op, char *params);
|
||||||
|
#endif
|
||||||
#include "mdhash.h"
|
#include "mdhash.h"
|
||||||
#ifdef WITH_SSL
|
#ifdef WITH_SSL
|
||||||
void ssl_install(void);
|
void ssl_install(void);
|
||||||
@ -35,7 +39,6 @@ _3proxy_mutex_t config_mutex;
|
|||||||
int haveerror = 0;
|
int haveerror = 0;
|
||||||
int linenum = 0;
|
int linenum = 0;
|
||||||
|
|
||||||
FILE *writable;
|
|
||||||
struct counter_header cheader = {"3CF", (time_t)0};
|
struct counter_header cheader = {"3CF", (time_t)0};
|
||||||
struct counter_record crecord;
|
struct counter_record crecord;
|
||||||
|
|
||||||
@ -60,10 +63,6 @@ FILE * confopen(){
|
|||||||
curconf += strlen(chrootp);
|
curconf += strlen(chrootp);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if(writable) {
|
|
||||||
rewind(writable);
|
|
||||||
return writable;
|
|
||||||
}
|
|
||||||
return fopen(curconf, "r");
|
return fopen(curconf, "r");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,12 +252,32 @@ static int h_proxy(int argc, unsigned char ** argv){
|
|||||||
childdef.service = S_UDPPM;
|
childdef.service = S_UDPPM;
|
||||||
childdef.helpmessage = " -s single packet UDP service for request/reply (DNS-like) services\n";
|
childdef.helpmessage = " -s single packet UDP service for request/reply (DNS-like) services\n";
|
||||||
}
|
}
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
else if(!strcmp((char *)argv[0], "admin")) {
|
else if(!strcmp((char *)argv[0], "admin")) {
|
||||||
childdef.pf = adminchild;
|
/* The same service as httpsrv, with the administration pages
|
||||||
|
declared for it. */
|
||||||
|
if(addhttprule("*", "/C*", "admin_counters", NULL) ||
|
||||||
|
addhttprule("*", "/R", "admin_reload", NULL) ||
|
||||||
|
addhttprule("*", "/S*", "admin_services", 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.port = 80;
|
||||||
childdef.isudp = 0;
|
childdef.isudp = 0;
|
||||||
childdef.service = S_ADMIN;
|
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")) {
|
else if(!strcmp((char *)argv[0], "dnspr")) {
|
||||||
childdef.pf = dnsprchild;
|
childdef.pf = dnsprchild;
|
||||||
childdef.port = 53;
|
childdef.port = 53;
|
||||||
@ -765,7 +784,6 @@ struct redirdesc redirs[] = {
|
|||||||
{R_SOCKS5P, "socks5+", sockschild},
|
{R_SOCKS5P, "socks5+", sockschild},
|
||||||
{R_SOCKS4B, "socks4b", sockschild},
|
{R_SOCKS4B, "socks4b", sockschild},
|
||||||
{R_SOCKS5B, "socks5b", sockschild},
|
{R_SOCKS5B, "socks5b", sockschild},
|
||||||
{R_ADMIN, "admin", adminchild},
|
|
||||||
{R_EXTIP, "extip", NULL},
|
{R_EXTIP, "extip", NULL},
|
||||||
{R_EXTPORT, "extport", NULL},
|
{R_EXTPORT, "extport", NULL},
|
||||||
{R_INTPORT, "intport", NULL},
|
{R_INTPORT, "intport", NULL},
|
||||||
@ -775,19 +793,53 @@ struct redirdesc redirs[] = {
|
|||||||
{0, NULL, NULL}
|
{0, NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
/* Installs one rule from code, for the pages a service predefines. */
|
||||||
|
static int addhttprule(char *host, char *url, char *op, 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->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) || parsepattern(&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. */
|
/* Parses an inclusive FIRST-LAST local port range into first | last << 16. */
|
||||||
static int parserange(unsigned char *arg, uint32_t *range)
|
static int parserange(unsigned char *arg, uint32_t *range)
|
||||||
{
|
{
|
||||||
char *end;
|
char *end;
|
||||||
unsigned long first, last;
|
unsigned long first, last;
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
first = strtoul((char *)arg, &end, 10);
|
first = strtoul((char *)arg, &end, 10);
|
||||||
if(errno || *end != '-' || !first || first > 65535) return 1;
|
if(end == (char *)arg || *end != '-' || !first || first > 65535) return 1;
|
||||||
|
|
||||||
errno = 0;
|
arg = (unsigned char *)end + 1;
|
||||||
last = strtoul(end + 1, &end, 10);
|
last = strtoul((char *)arg, &end, 10);
|
||||||
if(errno || *end || !last || last > 65535 || last < first) return 1;
|
if(end == (char *)arg || *end || !last || last > 65535 || last < first) return 1;
|
||||||
|
|
||||||
*range = (uint32_t)first | ((uint32_t)last << 16);
|
*range = (uint32_t)first | ((uint32_t)last << 16);
|
||||||
return 0;
|
return 0;
|
||||||
@ -906,6 +958,50 @@ static int h_parent(int argc, unsigned char **argv){
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
op = httpopbyname(argv[3]);
|
||||||
|
if(op < 0){
|
||||||
|
fprintf(stderr, "Unknown http operation: %s line %d\n", argv[3], linenum);
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
rule = malloc(sizeof(struct httprule));
|
||||||
|
if(!rule) return(21);
|
||||||
|
memset(rule, 0, sizeof(struct httprule));
|
||||||
|
rule->op = op;
|
||||||
|
|
||||||
|
if(parsepattern(&rule->host, argv[1]) || parsepattern(&rule->url, argv[2])){
|
||||||
|
fprintf(stderr, "No memory for http rule, line %d\n", linenum);
|
||||||
|
free(rule->host.name);
|
||||||
|
free(rule);
|
||||||
|
return(21);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(argc > 4){
|
||||||
|
rule->params = (unsigned char *)strdup((char *)argv[4]);
|
||||||
|
if(!rule->params){
|
||||||
|
free(rule->host.name);
|
||||||
|
free(rule->url.name);
|
||||||
|
free(rule);
|
||||||
|
return(21);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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){
|
static int h_nolog(int argc, unsigned char **argv){
|
||||||
struct ace *acl = NULL;
|
struct ace *acl = NULL;
|
||||||
|
|
||||||
@ -1044,20 +1140,7 @@ struct ace * make_ace (int argc, unsigned char ** argv){
|
|||||||
return(NULL);
|
return(NULL);
|
||||||
}
|
}
|
||||||
memset(hostnamel, 0, sizeof(struct hostname));
|
memset(hostnamel, 0, sizeof(struct hostname));
|
||||||
hostnamel->matchtype = 3;
|
if(parsepattern(hostnamel, arg)) {
|
||||||
pattern = arg;
|
|
||||||
if(pattern[arglen-1] == '*'){
|
|
||||||
arglen --;
|
|
||||||
pattern[arglen] = 0;
|
|
||||||
hostnamel->matchtype ^= MATCHEND;
|
|
||||||
}
|
|
||||||
if(pattern[0] == '*'){
|
|
||||||
pattern++;
|
|
||||||
arglen--;
|
|
||||||
hostnamel->matchtype ^= MATCHBEGIN;
|
|
||||||
}
|
|
||||||
hostnamel->name = (unsigned char *) strdup( (char *)pattern);
|
|
||||||
if(!hostnamel->name) {
|
|
||||||
fprintf(stderr, "No memory for ACL entry, line %d\n", linenum);
|
fprintf(stderr, "No memory for ACL entry, line %d\n", linenum);
|
||||||
return(NULL);
|
return(NULL);
|
||||||
}
|
}
|
||||||
@ -1721,7 +1804,13 @@ struct commands commandhandlers[]={
|
|||||||
{NULL, "socks", h_proxy, 1, 0},
|
{NULL, "socks", h_proxy, 1, 0},
|
||||||
{NULL, "tcppm", h_proxy, 4, 0},
|
{NULL, "tcppm", h_proxy, 4, 0},
|
||||||
{NULL, "udppm", h_proxy, 4, 0},
|
{NULL, "udppm", h_proxy, 4, 0},
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
{NULL, "admin", h_proxy, 1, 0},
|
{NULL, "admin", h_proxy, 1, 0},
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
{NULL, "httpsrv", h_proxy, 1, 0},
|
||||||
|
{NULL, "http", h_http, 4, 5},
|
||||||
|
#endif
|
||||||
{NULL, "dnspr", h_proxy, 1, 0},
|
{NULL, "dnspr", h_proxy, 1, 0},
|
||||||
{NULL, "internal", h_internal, 2, 2},
|
{NULL, "internal", h_internal, 2, 2},
|
||||||
{NULL, "external", h_external, 2, 2},
|
{NULL, "external", h_external, 2, 2},
|
||||||
@ -1969,16 +2058,6 @@ int readconfig(FILE * fp){
|
|||||||
if(!strcmp((char *)argv[0], "end") && argc == 1) {
|
if(!strcmp((char *)argv[0], "end") && argc == 1) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if(!strcmp((char *)argv[0], "writable") && argc == 1) {
|
|
||||||
if(!writable){
|
|
||||||
writable = freopen(curconf, "r+", fp);
|
|
||||||
if(!writable){
|
|
||||||
fprintf(stderr, "Unable to reopen config for writing: %s\n", curconf);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = 1;
|
res = 1;
|
||||||
for(cm = commandhandlers; cm; cm = cm->next){
|
for(cm = commandhandlers; cm; cm = cm->next){
|
||||||
@ -2140,7 +2219,7 @@ int reload (void){
|
|||||||
if(error) {
|
if(error) {
|
||||||
freeconf(&conf);
|
freeconf(&conf);
|
||||||
}
|
}
|
||||||
if(!writable)fclose(fp);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
_3proxy_mutex_unlock(&config_mutex);
|
_3proxy_mutex_unlock(&config_mutex);
|
||||||
return error;
|
return error;
|
||||||
|
|||||||
517
src/httpsrv.c
Normal file
517
src/httpsrv.c
Normal file
@ -0,0 +1,517 @@
|
|||||||
|
/*
|
||||||
|
3proxy - HTTP server
|
||||||
|
|
||||||
|
A small HTTP/1.0 server. The request is parsed into a struct httpreq and
|
||||||
|
handed to a handler chosen from a table by path, so the transport, request
|
||||||
|
parsing and response helpers are shared and a new endpoint is one row in
|
||||||
|
httphandlers[] plus a function.
|
||||||
|
|
||||||
|
The handlers built today generate deterministic responses for the regression
|
||||||
|
tests: /echo describes the connection as seen by the server, which is how a
|
||||||
|
test tells which source address and port a request arrived from, and /data
|
||||||
|
produces a requested amount of output.
|
||||||
|
|
||||||
|
Parsing here is deliberately blunt - fixed buffers, bounded reads, no shared
|
||||||
|
request parser - so that a fault in the code under test cannot be cancelled
|
||||||
|
out by the same fault in the server used to observe it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "proxy.h"
|
||||||
|
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#define RETURN(xxx) { param->res = xxx; goto CLEANRET; }
|
||||||
|
|
||||||
|
#define HTTPSRV_LINE 1024
|
||||||
|
#define HTTPSRV_BLOCK 8192
|
||||||
|
#define HTTPSRV_MAXHDR 64
|
||||||
|
|
||||||
|
|
||||||
|
/* Returns the value of a query parameter, or def when it is missing or not a
|
||||||
|
number. Values are clamped by the caller, not here. */
|
||||||
|
/* Copies a request field, refusing anything that does not fit rather than
|
||||||
|
storing a shortened copy. strncpy would leave the result unterminated at
|
||||||
|
exactly the length that overflows - it is only safe with a zeroed struct -
|
||||||
|
and a truncated path or host is worse than a rejected one, since it would be
|
||||||
|
matched against the rules as though the client had sent the shorter string.
|
||||||
|
*/
|
||||||
|
static int hexval(int c)
|
||||||
|
{
|
||||||
|
if(c >= '0' && c <= '9') return c - '0';
|
||||||
|
if(c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||||
|
if(c >= 'A' && c <= 'F') return c - 'A' + 10;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decodes %XX sequences. A malformed sequence, or one decoding to a NUL that
|
||||||
|
would cut the path short, is refused rather than passed on. */
|
||||||
|
static int urldecode(char *dst, size_t size, const char *src)
|
||||||
|
{
|
||||||
|
size_t o = 0;
|
||||||
|
|
||||||
|
while(*src){
|
||||||
|
int c = (unsigned char)*src++;
|
||||||
|
|
||||||
|
if(c == '%'){
|
||||||
|
int hi, lo;
|
||||||
|
|
||||||
|
hi = hexval((unsigned char)src[0]);
|
||||||
|
if(hi < 0) return 1;
|
||||||
|
lo = hexval((unsigned char)src[1]);
|
||||||
|
if(lo < 0) return 1;
|
||||||
|
c = (hi << 4) | lo;
|
||||||
|
src += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!c) return 1;
|
||||||
|
if(o + 1 >= size) return 1;
|
||||||
|
dst[o++] = (char)c;
|
||||||
|
}
|
||||||
|
|
||||||
|
dst[o] = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checked after decoding, because the encoded form hides both of these. */
|
||||||
|
static int pathunsafe(const char *path)
|
||||||
|
{
|
||||||
|
if(strstr(path, "/..")) return 1;
|
||||||
|
if(strchr(path, '\r') || strchr(path, '\n')) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int copyfield(char *dst, size_t size, const char *src)
|
||||||
|
{
|
||||||
|
size_t len = strlen(src);
|
||||||
|
|
||||||
|
if(len >= size) return 1;
|
||||||
|
memcpy(dst, src, len + 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long qparam(const char *query, const char *name, long def)
|
||||||
|
{
|
||||||
|
const char *p;
|
||||||
|
size_t len;
|
||||||
|
char *end;
|
||||||
|
long val;
|
||||||
|
|
||||||
|
if(!query || !*query) return def;
|
||||||
|
len = strlen(name);
|
||||||
|
|
||||||
|
for(p = query; *p; ){
|
||||||
|
if(!strncmp(p, name, len) && p[len] == '='){
|
||||||
|
val = strtol(p + len + 1, &end, 10);
|
||||||
|
if(end == p + len + 1) return def;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
p = strchr(p, '&');
|
||||||
|
if(!p) break;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int httpsrv_send(struct httpreq *r, const char *buf, int len)
|
||||||
|
{
|
||||||
|
return socksend(r->param, r->param->clisock, (unsigned char *)buf, len,
|
||||||
|
conf.timeouts[STRING_S]) != len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int httpsrv_printf(struct httpreq *r, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
char buf[HTTPSRV_LINE];
|
||||||
|
int len;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
len = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
if(len < 0) return 1;
|
||||||
|
if(len > (int)sizeof(buf) - 1) len = (int)sizeof(buf) - 1;
|
||||||
|
|
||||||
|
return httpsrv_send(r, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Writes the status line and headers. A negative length asks for chunked
|
||||||
|
encoding, which is how a response of unknown or deliberately unstated size is
|
||||||
|
produced. */
|
||||||
|
static int httpsrv_head(struct httpreq *r, int status, const char *ctype, long len)
|
||||||
|
{
|
||||||
|
const char *text;
|
||||||
|
|
||||||
|
switch(status){
|
||||||
|
case 200: text = "OK"; break;
|
||||||
|
case 204: text = "No Content"; break;
|
||||||
|
case 400: text = "Bad Request"; break;
|
||||||
|
case 404: text = "Not Found"; break;
|
||||||
|
case 500: text = "Internal Server Error"; break;
|
||||||
|
case 503: text = "Service Unavailable"; break;
|
||||||
|
default: text = "Unknown"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(httpsrv_printf(r, "HTTP/1.0 %d %s\r\n", status, text)) return 1;
|
||||||
|
if(httpsrv_printf(r, "Content-Type: %s\r\n", ctype)) return 1;
|
||||||
|
if(len >= 0){
|
||||||
|
if(httpsrv_printf(r, "Content-Length: %ld\r\n", len)) return 1;
|
||||||
|
}
|
||||||
|
else if(httpsrv_printf(r, "Transfer-Encoding: chunked\r\n")) return 1;
|
||||||
|
|
||||||
|
return httpsrv_printf(r, "Connection: close\r\n\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wraps one block as a chunk, a zero length writing the terminating chunk.
|
||||||
|
Takes the client rather than a request so that anything writing a chunked
|
||||||
|
response can use it. */
|
||||||
|
int httpchunk(struct clientparam *param, const char *buf, int len)
|
||||||
|
{
|
||||||
|
char hdr[16];
|
||||||
|
int hlen;
|
||||||
|
|
||||||
|
if(len <= 0){
|
||||||
|
return socksend(param, param->clisock, (unsigned char *)"0\r\n\r\n", 5,
|
||||||
|
conf.timeouts[STRING_S]) != 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
hlen = sprintf(hdr, "%x\r\n", len);
|
||||||
|
if(socksend(param, param->clisock, (unsigned char *)hdr, hlen,
|
||||||
|
conf.timeouts[STRING_S]) != hlen) return 1;
|
||||||
|
if(socksend(param, param->clisock, (unsigned char *)buf, len,
|
||||||
|
conf.timeouts[STRING_S]) != len) return 1;
|
||||||
|
|
||||||
|
return socksend(param, param->clisock, (unsigned char *)"\r\n", 2,
|
||||||
|
conf.timeouts[STRING_S]) != 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fills buf with a repeating pattern carrying its own offset, so a truncated or
|
||||||
|
reordered body is visible in the output rather than looking like a short
|
||||||
|
read. */
|
||||||
|
static void httpsrv_fill(char *buf, int len, unsigned long offset)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < len; i++){
|
||||||
|
unsigned long pos = offset + (unsigned long)i;
|
||||||
|
|
||||||
|
buf[i] = (pos % 64 == 63)? '\n' : (char)('0' + (int)((pos / 64) % 10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int op_echo(struct httpreq *r, const unsigned char *params)
|
||||||
|
{
|
||||||
|
struct clientparam *param = r->param;
|
||||||
|
char addr[64];
|
||||||
|
char body[HTTPSRV_LINE * 2];
|
||||||
|
int len;
|
||||||
|
PROXYSOCKADDRTYPE sa;
|
||||||
|
SASIZETYPE sasize = sizeof(sa);
|
||||||
|
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
if(param->srv->so._getpeername(param->sostate, param->clisock,
|
||||||
|
(struct sockaddr *)&sa, &sasize) ||
|
||||||
|
!myinet_ntop(*SAFAMILY(&sa), SAADDR(&sa), addr, sizeof(addr))){
|
||||||
|
strcpy(addr, "unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
len = snprintf(body, sizeof(body),
|
||||||
|
"peer.addr=%s\n"
|
||||||
|
"peer.port=%hu\n"
|
||||||
|
"method=%s\n"
|
||||||
|
"path=%s\n"
|
||||||
|
"query=%s\n"
|
||||||
|
"host=%s\n"
|
||||||
|
"content.length=%lu\n"
|
||||||
|
"glob.start=%d\n"
|
||||||
|
"glob.len=%d\n"
|
||||||
|
"glob=%.*s\n",
|
||||||
|
addr, ntohs(*SAPORT(&sa)), r->method, r->path, r->query,
|
||||||
|
r->host, r->contentlen, r->globstart, r->globlen,
|
||||||
|
r->globlen, r->path + r->globstart);
|
||||||
|
|
||||||
|
if(len < 0) return 1;
|
||||||
|
if(len > (int)sizeof(body) - 1) len = (int)sizeof(body) - 1;
|
||||||
|
|
||||||
|
if(httpsrv_head(r, 200, "text/plain", (long)len)) return 1;
|
||||||
|
return httpsrv_send(r, body, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* /data?size=N&chunked=0|1&status=NNN&block=N&delay=ms
|
||||||
|
Produces exactly N bytes of body. */
|
||||||
|
static int op_data(struct httpreq *r, const unsigned char *params)
|
||||||
|
{
|
||||||
|
char buf[HTTPSRV_BLOCK];
|
||||||
|
long size, block, delay, status;
|
||||||
|
int chunked;
|
||||||
|
unsigned long sent = 0;
|
||||||
|
|
||||||
|
size = qparam((const char *)params, "size", 0);
|
||||||
|
size = qparam(r->query, "size", size);
|
||||||
|
if(size < 0) size = 0;
|
||||||
|
block = qparam((const char *)params, "block", HTTPSRV_BLOCK);
|
||||||
|
block = qparam(r->query, "block", block);
|
||||||
|
if(block < 1 || block > HTTPSRV_BLOCK) block = HTTPSRV_BLOCK;
|
||||||
|
status = qparam((const char *)params, "status", 200);
|
||||||
|
status = qparam(r->query, "status", status);
|
||||||
|
if(status < 100 || status > 599) status = 200;
|
||||||
|
chunked = qparam(r->query, "chunked", qparam((const char *)params, "chunked", 0)) != 0;
|
||||||
|
delay = qparam(r->query, "delay", qparam((const char *)params, "delay", 0));
|
||||||
|
|
||||||
|
if(httpsrv_head(r, (int)status, "application/octet-stream",
|
||||||
|
chunked? -1 : size)) return 1;
|
||||||
|
|
||||||
|
while(sent < (unsigned long)size){
|
||||||
|
int len = (int)block;
|
||||||
|
|
||||||
|
if((unsigned long)len > (unsigned long)size - sent) len = (int)(size - sent);
|
||||||
|
httpsrv_fill(buf, len, sent);
|
||||||
|
|
||||||
|
if(delay > 0){
|
||||||
|
#ifdef _WIN32
|
||||||
|
usleep(delay);
|
||||||
|
#else
|
||||||
|
usleep(delay * 1000);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if(chunked){
|
||||||
|
if(httpchunk(r->param, buf, len)) return 1;
|
||||||
|
}
|
||||||
|
else if(httpsrv_send(r, buf, len)) return 1;
|
||||||
|
|
||||||
|
sent += (unsigned long)len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(chunked) return httpchunk(r->param, NULL, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int op_authrequired(struct httpreq *r)
|
||||||
|
{
|
||||||
|
static const char body[] = "authentication required\n";
|
||||||
|
|
||||||
|
if(httpsrv_printf(r, "HTTP/1.0 401 Authentication Required\r\n"
|
||||||
|
"WWW-Authenticate: Basic realm=\"3proxy\"\r\n"
|
||||||
|
"Content-Type: text/plain\r\n"
|
||||||
|
"Content-Length: %d\r\n"
|
||||||
|
"Connection: close\r\n\r\n", (int)sizeof(body) - 1)) return 1;
|
||||||
|
return httpsrv_send(r, body, (int)sizeof(body) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int op_forbidden(struct httpreq *r)
|
||||||
|
{
|
||||||
|
static const char body[] = "forbidden\n";
|
||||||
|
|
||||||
|
if(httpsrv_head(r, 403, "text/plain", (long)sizeof(body) - 1)) return 1;
|
||||||
|
return httpsrv_send(r, body, (int)sizeof(body) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int op_badrequest(struct httpreq *r)
|
||||||
|
{
|
||||||
|
static const char body[] = "bad request\n";
|
||||||
|
|
||||||
|
if(httpsrv_head(r, 400, "text/plain", (long)sizeof(body) - 1)) return 1;
|
||||||
|
return httpsrv_send(r, body, (int)sizeof(body) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int op_notfound(struct httpreq *r)
|
||||||
|
{
|
||||||
|
static const char body[] = "not found\n";
|
||||||
|
|
||||||
|
if(httpsrv_head(r, 404, "text/plain", (long)sizeof(body) - 1)) return 1;
|
||||||
|
return httpsrv_send(r, body, (int)sizeof(body) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Operations an http line can name. The rule supplies the parameters, so the
|
||||||
|
same operation serves different content on different urls. */
|
||||||
|
static struct httpop {
|
||||||
|
const char *name;
|
||||||
|
int (*fn)(struct httpreq *, const unsigned char *params);
|
||||||
|
} httpops[] = {
|
||||||
|
{"echo", op_echo},
|
||||||
|
{"data", op_data},
|
||||||
|
{"admin", op_admin},
|
||||||
|
{"admin_counters", op_admin_counters},
|
||||||
|
{"admin_reload", op_admin_reload},
|
||||||
|
{"admin_services", op_admin_services},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
void freehttprules(struct httprule *rule)
|
||||||
|
{
|
||||||
|
struct httprule *next;
|
||||||
|
|
||||||
|
while(rule){
|
||||||
|
next = rule->next;
|
||||||
|
if(rule->host.name) free(rule->host.name);
|
||||||
|
if(rule->url.name) free(rule->url.name);
|
||||||
|
if(rule->params) free(rule->params);
|
||||||
|
free(rule);
|
||||||
|
rule = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int httpopbyname(const unsigned char *name)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; httpops[i].name; i++){
|
||||||
|
if(!strcmp((char *)name, httpops[i].name)) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void * httpsrvchild(struct clientparam *param)
|
||||||
|
{
|
||||||
|
struct httpreq r;
|
||||||
|
char buf[HTTPSRV_LINE];
|
||||||
|
char *sp, *q;
|
||||||
|
struct httprule *rule;
|
||||||
|
int i, hdrs = 0;
|
||||||
|
|
||||||
|
memset(&r, 0, sizeof(r));
|
||||||
|
r.param = param;
|
||||||
|
|
||||||
|
i = sockgetlinebuf(param, CLIENT, (unsigned char *)buf, sizeof(buf) - 1, '\n',
|
||||||
|
conf.timeouts[STRING_S]);
|
||||||
|
if(i < 5) RETURN(701);
|
||||||
|
buf[i] = 0;
|
||||||
|
|
||||||
|
sp = strchr(buf, ' ');
|
||||||
|
if(!sp) RETURN(702);
|
||||||
|
*sp = 0;
|
||||||
|
if(copyfield(r.method, sizeof(r.method), buf)) RETURN(703);
|
||||||
|
|
||||||
|
if(!strcasecmp(r.method, "GET")) param->operation = HTTP_GET;
|
||||||
|
else if(!strcasecmp(r.method, "POST")) param->operation = HTTP_POST;
|
||||||
|
else if(!strcasecmp(r.method, "PUT")) param->operation = HTTP_PUT;
|
||||||
|
else if(!strcasecmp(r.method, "HEAD")) param->operation = HTTP_HEAD;
|
||||||
|
else param->operation = HTTP_OTHER;
|
||||||
|
|
||||||
|
while(*++sp == ' ');
|
||||||
|
q = strchr(sp, ' ');
|
||||||
|
if(q) *q = 0;
|
||||||
|
q = sp + strcspn(sp, "\r\n");
|
||||||
|
*q = 0;
|
||||||
|
|
||||||
|
q = strchr(sp, '?');
|
||||||
|
if(q){
|
||||||
|
*q = 0;
|
||||||
|
if(copyfield(r.query, sizeof(r.query), q + 1)) RETURN(704);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
char decoded[sizeof(r.path)];
|
||||||
|
|
||||||
|
/* Keep the raw path first so a refused request still records what
|
||||||
|
was asked for. */
|
||||||
|
if(copyfield(r.path, sizeof(r.path), sp)) RETURN(705);
|
||||||
|
if(urldecode(decoded, sizeof(decoded), sp)) RETURN(707);
|
||||||
|
if(pathunsafe(decoded)) RETURN(708);
|
||||||
|
strcpy(r.path, decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
while(hdrs++ < HTTPSRV_MAXHDR &&
|
||||||
|
(i = sockgetlinebuf(param, CLIENT, (unsigned char *)buf, sizeof(buf) - 1,
|
||||||
|
'\n', conf.timeouts[STRING_S])) > 2){
|
||||||
|
buf[i] = 0;
|
||||||
|
if(!strncasecmp(buf, "host:", 5)){
|
||||||
|
sp = buf + 5;
|
||||||
|
while(isspace((unsigned char)*sp)) sp++;
|
||||||
|
sp[strcspn(sp, "\r\n")] = 0;
|
||||||
|
if(copyfield(r.host, sizeof(r.host), sp)) RETURN(706);
|
||||||
|
}
|
||||||
|
else if(!strncasecmp(buf, "authorization:", 14)){
|
||||||
|
char creds[256];
|
||||||
|
int clen;
|
||||||
|
|
||||||
|
sp = buf + 14;
|
||||||
|
while(isspace((unsigned char)*sp)) sp++;
|
||||||
|
if(strncasecmp(sp, "basic", 5)) continue;
|
||||||
|
sp += 5;
|
||||||
|
while(isspace((unsigned char)*sp)) sp++;
|
||||||
|
sp[strcspn(sp, "\r\n")] = 0;
|
||||||
|
|
||||||
|
clen = de64((unsigned char *)sp, (unsigned char *)creds, sizeof(creds) - 1);
|
||||||
|
if(clen <= 0) continue;
|
||||||
|
creds[clen] = 0;
|
||||||
|
|
||||||
|
q = strchr(creds, ':');
|
||||||
|
if(q){
|
||||||
|
*q = 0;
|
||||||
|
if(param->password) free(param->password);
|
||||||
|
param->password = (unsigned char *)strdup(q + 1);
|
||||||
|
}
|
||||||
|
if(param->username) free(param->username);
|
||||||
|
param->username = (unsigned char *)strdup(creds);
|
||||||
|
}
|
||||||
|
else if(!strncasecmp(buf, "content-length:", 15)){
|
||||||
|
sscanf(buf + 15, "%lu", &r.contentlen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(r.host[0]){
|
||||||
|
char host[sizeof(r.host)];
|
||||||
|
char *colon;
|
||||||
|
|
||||||
|
/* Access rules match a bare name, so drop the port the client sent.
|
||||||
|
An address in brackets keeps its colons. */
|
||||||
|
strcpy(host, r.host);
|
||||||
|
colon = (*host == '[')? strchr(host, ']') : host;
|
||||||
|
if(colon){
|
||||||
|
colon = strchr(colon, ':');
|
||||||
|
if(colon) *colon = 0;
|
||||||
|
}
|
||||||
|
if(*host == '['){
|
||||||
|
memmove(host, host + 1, strlen(host));
|
||||||
|
colon = strchr(host, ']');
|
||||||
|
if(colon) *colon = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(param->hostname) free(param->hostname);
|
||||||
|
param->hostname = (unsigned char *)strdup(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The request is answered here, so the address it was sent to is the
|
||||||
|
destination an access rule should match. Authorization skips doconnect
|
||||||
|
for this service, so naming a destination cannot start a connection. */
|
||||||
|
param->req = param->sincl;
|
||||||
|
|
||||||
|
i = (*param->srv->authfunc)(param);
|
||||||
|
if(i && i != 10){
|
||||||
|
/* 4 no credentials, 5 unknown user, 6 wrong password: all of them
|
||||||
|
should let the client offer credentials again. */
|
||||||
|
if(i >= 4 && i <= 6) op_authrequired(&r);
|
||||||
|
else op_forbidden(&r);
|
||||||
|
RETURN(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(rule = param->srv->httprules; rule; rule = rule->next){
|
||||||
|
if(patternmatch(&rule->host, (unsigned char *)r.host) &&
|
||||||
|
patternmatchpos(&rule->url, (unsigned char *)r.path,
|
||||||
|
&r.globstart, &r.globlen)){
|
||||||
|
httpops[rule->op].fn(&r, rule->params);
|
||||||
|
RETURN(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
op_notfound(&r);
|
||||||
|
RETURN(404);
|
||||||
|
|
||||||
|
CLEANRET:
|
||||||
|
if(param->res >= 700 && param->res < 800) op_badrequest(&r);
|
||||||
|
/* Log the request the way the proxy does: the parameters decide what was
|
||||||
|
served, so a bare path is not enough to explain a response. */
|
||||||
|
{
|
||||||
|
char logbuf[sizeof(r.method) + sizeof(r.host) + sizeof(r.path) +
|
||||||
|
sizeof(r.query) + 8];
|
||||||
|
|
||||||
|
sprintf(logbuf, "%s %s %s%s%s", r.method[0]? r.method : "-",
|
||||||
|
r.host[0]? r.host : "-", r.path,
|
||||||
|
r.query[0]? "?" : "", r.query);
|
||||||
|
dolog(param, (unsigned char *)logbuf);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -15,7 +15,9 @@ void decodeurl(unsigned char *s, int allowcr);
|
|||||||
int parsestr (unsigned char *str, unsigned char **argm, int nitems, unsigned char ** buff, int *inbuf, int *bufsize);
|
int parsestr (unsigned char *str, unsigned char **argm, int nitems, unsigned char ** buff, int *inbuf, int *bufsize);
|
||||||
struct ace * make_ace (int argc, unsigned char ** argv);
|
struct ace * make_ace (int argc, unsigned char ** argv);
|
||||||
extern char * proxy_stringtable[];
|
extern char * proxy_stringtable[];
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
extern char * admin_stringtable[];
|
extern char * admin_stringtable[];
|
||||||
|
#endif
|
||||||
extern struct schedule * schedule;
|
extern struct schedule * schedule;
|
||||||
int start_proxy_thread(struct child * chp);
|
int start_proxy_thread(struct child * chp);
|
||||||
|
|
||||||
@ -59,7 +61,6 @@ struct symbol symbols[] = {
|
|||||||
{symbols+34, "socks", (void *) sockschild},
|
{symbols+34, "socks", (void *) sockschild},
|
||||||
{symbols+35, "tcppm", (void *) tcppmchild},
|
{symbols+35, "tcppm", (void *) tcppmchild},
|
||||||
{symbols+36, "udppm", (void *) udppmchild},
|
{symbols+36, "udppm", (void *) udppmchild},
|
||||||
{symbols+37, "admin", (void *) adminchild},
|
|
||||||
{symbols+38, "ftppr", (void *) ftpprchild},
|
{symbols+38, "ftppr", (void *) ftpprchild},
|
||||||
{symbols+39, "smtpp", (void *) smtppchild},
|
{symbols+39, "smtpp", (void *) smtppchild},
|
||||||
{symbols+40, "auto", (void *) smtppchild},
|
{symbols+40, "auto", (void *) smtppchild},
|
||||||
@ -121,7 +122,11 @@ struct pluginlink pluginlink = {
|
|||||||
proxy_stringtable,
|
proxy_stringtable,
|
||||||
&schedule,
|
&schedule,
|
||||||
freeacl,
|
freeacl,
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
admin_stringtable,
|
admin_stringtable,
|
||||||
|
#else
|
||||||
|
NULL,
|
||||||
|
#endif
|
||||||
&childdef,
|
&childdef,
|
||||||
start_proxy_thread,
|
start_proxy_thread,
|
||||||
freeparam,
|
freeparam,
|
||||||
|
|||||||
16
src/proxy.h
16
src/proxy.h
@ -353,6 +353,9 @@ int readconfig(FILE * fp);
|
|||||||
void initcommands(void);
|
void initcommands(void);
|
||||||
int connectwithpoll(struct clientparam *param, SOCKET sock, struct sockaddr *sa, SASIZETYPE size, int to);
|
int connectwithpoll(struct clientparam *param, SOCKET sock, struct sockaddr *sa, SASIZETYPE size, int to);
|
||||||
int bindwithrange(struct clientparam *param, SOCKET sock, PROXYSOCKADDRTYPE *sa, uint32_t range);
|
int bindwithrange(struct clientparam *param, SOCKET sock, PROXYSOCKADDRTYPE *sa, uint32_t range);
|
||||||
|
int parsepattern(struct hostname *h, unsigned char *arg);
|
||||||
|
int patternmatch(const struct hostname *h, const unsigned char *str);
|
||||||
|
int patternmatchpos(const struct hostname *h, const unsigned char *str, int *start, int *len);
|
||||||
void applyportranges(struct clientparam * param, struct ace * acentry);
|
void applyportranges(struct clientparam * param, struct ace * acentry);
|
||||||
|
|
||||||
|
|
||||||
@ -373,7 +376,18 @@ void * sockschild(struct clientparam * param);
|
|||||||
void * tcppmchild(struct clientparam * param);
|
void * tcppmchild(struct clientparam * param);
|
||||||
void * autochild(struct clientparam * param);
|
void * autochild(struct clientparam * param);
|
||||||
void * udppmchild(struct clientparam * param);
|
void * udppmchild(struct clientparam * param);
|
||||||
void * adminchild(struct clientparam * param);
|
#ifdef WITH_HTTPSRV
|
||||||
|
int op_admin(struct httpreq *r, const unsigned char *params);
|
||||||
|
int op_admin_counters(struct httpreq *r, const unsigned char *params);
|
||||||
|
int op_admin_reload(struct httpreq *r, const unsigned char *params);
|
||||||
|
int op_admin_services(struct httpreq *r, const unsigned char *params);
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
void * httpsrvchild(struct clientparam * param);
|
||||||
|
int httpopbyname(const unsigned char *name);
|
||||||
|
int httpchunk(struct clientparam *param, const char *buf, int len);
|
||||||
|
void freehttprules(struct httprule *rule);
|
||||||
|
#endif
|
||||||
void * ftpprchild(struct clientparam * param);
|
void * ftpprchild(struct clientparam * param);
|
||||||
void * tlsprchild(struct clientparam * param);
|
void * tlsprchild(struct clientparam * param);
|
||||||
/* Child functions return the child to redirect the request to, or NULL if
|
/* Child functions return the child to redirect the request to, or NULL if
|
||||||
|
|||||||
@ -414,6 +414,15 @@ int MODULEMAINFUNC (int argc, char** argv){
|
|||||||
#endif
|
#endif
|
||||||
srv.service = defparam.service = childdef.service;
|
srv.service = defparam.service = childdef.service;
|
||||||
|
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
/* http lines accumulate until a service claims them, so each httpsrv takes
|
||||||
|
the rules written above it and the next one starts empty. */
|
||||||
|
if(srv.service == S_HTTPSRV){
|
||||||
|
srv.httprules = conf.httprules;
|
||||||
|
conf.httprules = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef STDMAIN
|
#ifndef STDMAIN
|
||||||
if(conf.acl){
|
if(conf.acl){
|
||||||
srv.acl = copyacl(conf.acl);
|
srv.acl = copyacl(conf.acl);
|
||||||
@ -1335,6 +1344,9 @@ void srvfree(struct srvparam * srv){
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(srv->acl)freeacl(srv->acl);
|
if(srv->acl)freeacl(srv->acl);
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
if(srv->httprules)freehttprules(srv->httprules);
|
||||||
|
#endif
|
||||||
if(srv->authfuncs)freeauth(srv->authfuncs);
|
if(srv->authfuncs)freeauth(srv->authfuncs);
|
||||||
#endif
|
#endif
|
||||||
_3proxy_mutex_destroy(&srv->counter_mutex);
|
_3proxy_mutex_destroy(&srv->counter_mutex);
|
||||||
|
|||||||
@ -216,6 +216,7 @@ typedef enum {
|
|||||||
S_AUTO,
|
S_AUTO,
|
||||||
S_TLSPR,
|
S_TLSPR,
|
||||||
S_IMAPP,
|
S_IMAPP,
|
||||||
|
S_HTTPSRV,
|
||||||
S_ZOMBIE
|
S_ZOMBIE
|
||||||
}PROXYSERVICE;
|
}PROXYSERVICE;
|
||||||
|
|
||||||
@ -356,6 +357,28 @@ struct hostname {
|
|||||||
int matchtype;
|
int matchtype;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* A request handed to an http operation. */
|
||||||
|
struct httpreq {
|
||||||
|
struct clientparam *param;
|
||||||
|
char method[16];
|
||||||
|
char path[256];
|
||||||
|
char query[512];
|
||||||
|
char host[256];
|
||||||
|
unsigned long contentlen;
|
||||||
|
int globstart, globlen;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* One "http" line: which host and url it answers for, which operation serves
|
||||||
|
it and the parameters that operation takes. Patterns use the same syntax and
|
||||||
|
the same matcher as host lists in access rules. */
|
||||||
|
struct httprule {
|
||||||
|
struct httprule *next;
|
||||||
|
struct hostname host;
|
||||||
|
struct hostname url;
|
||||||
|
int op;
|
||||||
|
unsigned char *params;
|
||||||
|
};
|
||||||
|
|
||||||
struct ace {
|
struct ace {
|
||||||
struct ace *next;
|
struct ace *next;
|
||||||
int action;
|
int action;
|
||||||
@ -583,6 +606,9 @@ struct srvparam {
|
|||||||
struct auth *authenticate;
|
struct auth *authenticate;
|
||||||
struct pollfd * srvfds;
|
struct pollfd * srvfds;
|
||||||
struct ace *acl;
|
struct ace *acl;
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
struct httprule *httprules;
|
||||||
|
#endif
|
||||||
struct auth *authfuncs;
|
struct auth *authfuncs;
|
||||||
struct filter *filter;
|
struct filter *filter;
|
||||||
unsigned char * logformat;
|
unsigned char * logformat;
|
||||||
@ -702,6 +728,9 @@ struct extparam {
|
|||||||
_3proxy_sem_t threadinit;
|
_3proxy_sem_t threadinit;
|
||||||
int *timeouts;
|
int *timeouts;
|
||||||
struct ace * acl;
|
struct ace * acl;
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
struct httprule *httprules;
|
||||||
|
#endif
|
||||||
char * conffile;
|
char * conffile;
|
||||||
struct bandlim * bandlimiter, *bandlimiterout;
|
struct bandlim * bandlimiter, *bandlimiterout;
|
||||||
struct connlim * connlimiter;
|
struct connlim * connlimiter;
|
||||||
|
|||||||
256
src/webadmin.c
256
src/webadmin.c
@ -8,11 +8,12 @@
|
|||||||
|
|
||||||
#include "proxy.h"
|
#include "proxy.h"
|
||||||
|
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
|
||||||
#define RETURN(xxx) { param->res = xxx; goto CLEANRET; }
|
#define RETURN(xxx) { param->res = xxx; goto CLEANRET; }
|
||||||
|
|
||||||
#define LINESIZE 65536
|
#define LINESIZE 65536
|
||||||
|
|
||||||
extern FILE *writable;
|
|
||||||
FILE * confopen();
|
FILE * confopen();
|
||||||
extern void decodeurl(unsigned char *s, int filter);
|
extern void decodeurl(unsigned char *s, int filter);
|
||||||
|
|
||||||
@ -211,8 +212,7 @@ char * admin_stringtable[]={
|
|||||||
" </h2>\r\n"
|
" </h2>\r\n"
|
||||||
"<A HREF=\'/C'>Counters</A><br>\r\n"
|
"<A HREF=\'/C'>Counters</A><br>\r\n"
|
||||||
"<A HREF=\'/R'>Reload</A><br>\r\n"
|
"<A HREF=\'/R'>Reload</A><br>\r\n"
|
||||||
"<A HREF=\'/S'>Running Services</A><br>\r\n"
|
"<A HREF=\'/S'>Running Services</A>\r\n"
|
||||||
"<A HREF=\'/F'>Config</A>\r\n"
|
|
||||||
"</td><td>"
|
"</td><td>"
|
||||||
"<h2>%s %s configuration</h2>",
|
"<h2>%s %s configuration</h2>",
|
||||||
|
|
||||||
@ -367,92 +367,62 @@ static int printiplist(char *buf, int bufsize, struct iplist* ipl, char * delim)
|
|||||||
return printed;
|
return printed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void * adminchild(struct clientparam* param) {
|
/* The admin pages are http operations: the service, request parsing and
|
||||||
int i, res;
|
authorization belong to httpsrv, and what is left here is the page itself.
|
||||||
char * buf;
|
A star in the url carries the selector the pages used to read out of the
|
||||||
char username[256];
|
path, so /C with a star gives D2 or S2 to disable or enable a counter. */
|
||||||
char *sb;
|
|
||||||
char *req = NULL;
|
|
||||||
struct printparam pp;
|
|
||||||
unsigned contentlen = 0;
|
|
||||||
int isform = 0;
|
|
||||||
int limited = 0;
|
|
||||||
|
|
||||||
|
static char * admin_open(struct printparam *pp, struct clientparam *param)
|
||||||
|
{
|
||||||
|
char *buf;
|
||||||
|
|
||||||
limited =param->srv->s_option;
|
pp->inbuf = 0;
|
||||||
pp.inbuf = 0;
|
pp->cp = param;
|
||||||
pp.cp = param;
|
|
||||||
|
|
||||||
buf = malloc(LINESIZE);
|
buf = malloc(LINESIZE);
|
||||||
if(!buf) {RETURN(555);}
|
if(!buf) return NULL;
|
||||||
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]!='/') &&
|
sprintf(buf, ok, conf.stringtable?(char *)conf.stringtable[2]:"3proxy",
|
||||||
(buf[0]!='P' || buf[1]!='O' || buf[2]!='S' || buf[3]!='T' || buf[4]!=' ' || buf[5]!='/')))
|
conf.stringtable?(char *)conf.stringtable[2]:"3[APA3A] tiny proxy",
|
||||||
{
|
conf.stringtable?(char *)conf.stringtable[3]:"");
|
||||||
RETURN(701);
|
printstr(pp, buf);
|
||||||
}
|
return buf;
|
||||||
buf[i] = 0;
|
}
|
||||||
sb = strchr(buf+5, ' ');
|
|
||||||
if(!sb){
|
static void admin_close(struct printparam *pp, char *buf)
|
||||||
RETURN(702);
|
{
|
||||||
}
|
printstr(pp, tail);
|
||||||
*sb = 0;
|
printstr(pp, NULL);
|
||||||
req = strdup(buf + ((*buf == 'P')? 6 : 5));
|
if(buf) free(buf);
|
||||||
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))){
|
int op_admin(struct httpreq *r, const unsigned char *params)
|
||||||
sb = strchr(buf, ':');
|
{
|
||||||
if(!sb)continue;
|
struct printparam pp;
|
||||||
++sb;
|
char *buf;
|
||||||
while(isspace(*sb))sb++;
|
|
||||||
if(!*sb || strncasecmp(sb, "basic", 5)){
|
buf = admin_open(&pp, r->param);
|
||||||
continue;
|
if(!buf) return 1;
|
||||||
}
|
printstr(&pp, (char *)conf.stringtable[WEBBANNERS]);
|
||||||
sb+=5;
|
admin_close(&pp, buf);
|
||||||
while(isspace(*sb))sb++;
|
return 0;
|
||||||
i = de64((unsigned char *)sb, (unsigned char *)username, 255);
|
}
|
||||||
if(i<=0)continue;
|
|
||||||
username[i] = 0;
|
int op_admin_counters(struct httpreq *r, const unsigned char *params)
|
||||||
sb = strchr((char *)username, ':');
|
{
|
||||||
if(sb){
|
struct clientparam *param = r->param;
|
||||||
*sb = 0;
|
struct printparam pp;
|
||||||
if(param->password)free(param->password);
|
char *buf;
|
||||||
param->password = (unsigned char *)strdup(sb+1);
|
const char *sel;
|
||||||
}
|
int limited;
|
||||||
if(param->username) free(param->username);
|
|
||||||
param->username = (unsigned char *)strdup(username);
|
limited = param->srv->s_option;
|
||||||
continue;
|
/* In limited mode a counter may be looked at but not switched. */
|
||||||
}
|
sel = limited? "" : r->path + r->globstart;
|
||||||
else if(i > 15 && (!strncasecmp(buf, "content-length:", 15))){
|
|
||||||
sb = buf + 15;
|
buf = admin_open(&pp, param);
|
||||||
while(isspace(*sb))sb++;
|
if(!buf) return 1;
|
||||||
sscanf(sb, "%u", &contentlen);
|
|
||||||
if(contentlen > LINESIZE*1024) contentlen = 0;
|
|
||||||
}
|
|
||||||
else if(i > 13 && (!strncasecmp(buf, "content-type:", 13))){
|
|
||||||
sb = buf + 13;
|
|
||||||
while(isspace(*sb))sb++;
|
|
||||||
if(!strncasecmp(sb, "x-www-form-urlencoded", 21)) isform = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
param->operation = ADMIN;
|
|
||||||
if(isform && contentlen) {
|
|
||||||
printstr(&pp, "HTTP/1.0 100 Continue\r\n\r\n");
|
|
||||||
stdpr(&pp, NULL, 0);
|
|
||||||
}
|
|
||||||
res = (*param->srv->authfunc)(param);
|
|
||||||
if(res && res != 10) {
|
|
||||||
printstr(&pp, authreq);
|
|
||||||
RETURN(res);
|
|
||||||
}
|
|
||||||
if(limited || param->redirected){
|
|
||||||
if(*req == 'C') req[1] = 0;
|
|
||||||
else *req = 0;
|
|
||||||
}
|
|
||||||
sprintf(buf, ok, conf.stringtable?(char *)conf.stringtable[2]:"3proxy", conf.stringtable?(char *)conf.stringtable[2]:"3[APA3A] tiny proxy", conf.stringtable?(char *)conf.stringtable[3]:"");
|
|
||||||
if(*req != 'S') printstr(&pp, buf);
|
|
||||||
switch(*req){
|
|
||||||
case 'C':
|
|
||||||
printstr(&pp, counters);
|
printstr(&pp, counters);
|
||||||
{
|
{
|
||||||
struct trafcount *cp;
|
struct trafcount *cp;
|
||||||
@ -463,8 +433,8 @@ void * adminchild(struct clientparam* param) {
|
|||||||
if(cp->ace && (limited || param->redirected)){
|
if(cp->ace && (limited || param->redirected)){
|
||||||
if(!ACLmatches(cp->ace, param))continue;
|
if(!ACLmatches(cp->ace, param))continue;
|
||||||
}
|
}
|
||||||
if(req[1] == 'S' && atoi(req+2) == num) cp->disabled=0;
|
if(sel[0] == 'S' && atoi(sel+1) == num) cp->disabled=0;
|
||||||
if(req[1] == 'D' && atoi(req+2) == num) cp->disabled=1;
|
if(sel[0] == 'D' && atoi(sel+1) == num) cp->disabled=1;
|
||||||
inbuf += sprintf(buf, "<tr><td>%s</td><td>", cp->ace?aceaction(cp->ace->action):"-");
|
inbuf += sprintf(buf, "<tr><td>%s</td><td>", cp->ace?aceaction(cp->ace->action):"-");
|
||||||
if(cp->number || cp->comment)
|
if(cp->number || cp->comment)
|
||||||
inbuf += sprintf(buf+inbuf, "%d/%s</td>" , cp->number,
|
inbuf += sprintf(buf+inbuf, "%d/%s</td>" , cp->number,
|
||||||
@ -535,85 +505,55 @@ void * adminchild(struct clientparam* param) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
printstr(&pp, counterstail);
|
printstr(&pp, counterstail);
|
||||||
break;
|
|
||||||
|
|
||||||
case 'R':
|
admin_close(&pp, buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int op_admin_reload(struct httpreq *r, const unsigned char *params)
|
||||||
|
{
|
||||||
|
struct printparam pp;
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
buf = admin_open(&pp, r->param);
|
||||||
|
if(!buf) return 1;
|
||||||
|
|
||||||
|
if(r->param->srv->s_option) printstr(&pp, (char *)conf.stringtable[WEBBANNERS]);
|
||||||
|
else {
|
||||||
conf.needreload = 1;
|
conf.needreload = 1;
|
||||||
printstr(&pp, "<h3>Reload scheduled</h3>");
|
printstr(&pp, "<h3>Reload scheduled</h3>");
|
||||||
break;
|
|
||||||
case 'S':
|
|
||||||
{
|
|
||||||
if(req[1] == 'X'){
|
|
||||||
printstr(&pp, style);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
admin_close(&pp, buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int op_admin_services(struct httpreq *r, const unsigned char *params)
|
||||||
|
{
|
||||||
|
struct clientparam *param = r->param;
|
||||||
|
struct printparam pp;
|
||||||
|
char *buf;
|
||||||
|
const char *sel;
|
||||||
|
|
||||||
|
if(param->srv->s_option) return op_admin(r, params);
|
||||||
|
|
||||||
|
sel = r->path + r->globstart;
|
||||||
|
|
||||||
|
/* This page is xml, so it carries its own headers instead of the html
|
||||||
|
wrapper the other pages share. */
|
||||||
|
pp.inbuf = 0;
|
||||||
|
pp.cp = param;
|
||||||
|
buf = NULL;
|
||||||
|
|
||||||
|
if(sel[0] == 'X') printstr(&pp, style);
|
||||||
|
else {
|
||||||
printstr(&pp, xml);
|
printstr(&pp, xml);
|
||||||
printval(conf.services, TYPE_SERVER, 0, &pp);
|
printval(conf.services, TYPE_SERVER, 0, &pp);
|
||||||
printstr(&pp, postxml);
|
printstr(&pp, postxml);
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case 'F':
|
|
||||||
{
|
|
||||||
FILE *fp;
|
|
||||||
char buf[256];
|
|
||||||
|
|
||||||
fp = confopen();
|
|
||||||
if(!fp){
|
|
||||||
printstr(&pp, "<h3><font color=\"red\">Failed to open config file</font></h3>");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
printstr(&pp, "<h3>Please be careful editing config file remotely</h3>");
|
|
||||||
printstr(&pp, "<form method=\"POST\" action=\"/U\" enctype=\"application/x-www-form-urlencoded\"><textarea cols=\"80\" rows=\"30\" name=\"conffile\">");
|
|
||||||
while(fgets(buf, 256, fp)){
|
|
||||||
printstr(&pp, buf);
|
|
||||||
}
|
|
||||||
if(!writable) fclose(fp);
|
|
||||||
printstr(&pp, "</textarea><br><input type=\"Submit\"></form>");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'U':
|
|
||||||
{
|
|
||||||
unsigned l=0;
|
|
||||||
int error = 0;
|
|
||||||
|
|
||||||
if(!writable || !contentlen || fseek(writable, 0, 0)){
|
|
||||||
error = 1;
|
|
||||||
}
|
|
||||||
while(l < contentlen && (i = sockgetlinebuf(param, CLIENT, (unsigned char *)buf, (contentlen - l) > LINESIZE - 1?LINESIZE - 1:contentlen - l, '+', conf.timeouts[STRING_S])) > 0){
|
|
||||||
if((unsigned)i > (contentlen - l)) i = (contentlen - l);
|
|
||||||
if(!l){
|
|
||||||
if(i<9 || strncasecmp(buf, "conffile=", 9)) error = 1;
|
|
||||||
}
|
|
||||||
if(!error){
|
|
||||||
buf[i] = 0;
|
|
||||||
decodeurl((unsigned char *)buf, 1);
|
|
||||||
fprintf(writable, "%s", l? buf : buf + 9);
|
|
||||||
}
|
|
||||||
l += i;
|
|
||||||
}
|
|
||||||
if(writable && !error){
|
|
||||||
fflush(writable);
|
|
||||||
#ifndef _WINCE
|
|
||||||
if(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);
|
printstr(&pp, NULL);
|
||||||
if(buf) free(buf);
|
return 0;
|
||||||
dolog(param, (unsigned char *)req);
|
|
||||||
if(req)free(req);
|
|
||||||
return (NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user