mirror of
https://github.com/BlackLight/Snort_AIPreproc.git
synced 2024-11-27 14:15:13 +01:00
Adding webserver features
This commit is contained in:
parent
c854afe6f0
commit
8305581fa2
11 changed files with 684 additions and 8013 deletions
|
@ -27,7 +27,8 @@ outdb.c \
|
||||||
postgresql.c \
|
postgresql.c \
|
||||||
regex.c \
|
regex.c \
|
||||||
spp_ai.c \
|
spp_ai.c \
|
||||||
stream.c
|
stream.c \
|
||||||
|
webserv.c
|
||||||
|
|
||||||
ACLOCAL_AMFLAGS = -I m4
|
ACLOCAL_AMFLAGS = -I m4
|
||||||
EXTRA_DIST = README INSTALL ChangeLog AUTHORS COPYING Doxyfile NEWS TODO doc etc include uthash corr_rules *.h
|
EXTRA_DIST = README INSTALL ChangeLog AUTHORS COPYING Doxyfile NEWS TODO doc etc include uthash corr_rules *.h
|
||||||
|
|
14
Makefile.in
14
Makefile.in
|
@ -41,7 +41,10 @@ DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
|
||||||
$(top_srcdir)/configure AUTHORS COPYING ChangeLog INSTALL NEWS \
|
$(top_srcdir)/configure AUTHORS COPYING ChangeLog INSTALL NEWS \
|
||||||
TODO config.guess config.sub install-sh ltmain.sh missing
|
TODO config.guess config.sub install-sh ltmain.sh missing
|
||||||
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
||||||
am__aclocal_m4_deps = $(top_srcdir)/configure.ac
|
am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
|
||||||
|
$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
|
||||||
|
$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
|
||||||
|
$(top_srcdir)/configure.ac
|
||||||
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
|
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
|
||||||
$(ACLOCAL_M4)
|
$(ACLOCAL_M4)
|
||||||
am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
|
am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
|
||||||
|
@ -81,7 +84,8 @@ am_libsf_ai_preproc_la_OBJECTS = libsf_ai_preproc_la-alert_history.lo \
|
||||||
libsf_ai_preproc_la-correlation.lo libsf_ai_preproc_la-db.lo \
|
libsf_ai_preproc_la-correlation.lo libsf_ai_preproc_la-db.lo \
|
||||||
libsf_ai_preproc_la-mysql.lo libsf_ai_preproc_la-outdb.lo \
|
libsf_ai_preproc_la-mysql.lo libsf_ai_preproc_la-outdb.lo \
|
||||||
libsf_ai_preproc_la-postgresql.lo libsf_ai_preproc_la-regex.lo \
|
libsf_ai_preproc_la-postgresql.lo libsf_ai_preproc_la-regex.lo \
|
||||||
libsf_ai_preproc_la-spp_ai.lo libsf_ai_preproc_la-stream.lo
|
libsf_ai_preproc_la-spp_ai.lo libsf_ai_preproc_la-stream.lo \
|
||||||
|
libsf_ai_preproc_la-webserv.lo
|
||||||
nodist_libsf_ai_preproc_la_OBJECTS = \
|
nodist_libsf_ai_preproc_la_OBJECTS = \
|
||||||
libsf_ai_preproc_la-sf_dynamic_preproc_lib.lo \
|
libsf_ai_preproc_la-sf_dynamic_preproc_lib.lo \
|
||||||
libsf_ai_preproc_la-sfPolicyUserData.lo
|
libsf_ai_preproc_la-sfPolicyUserData.lo
|
||||||
|
@ -262,7 +266,8 @@ outdb.c \
|
||||||
postgresql.c \
|
postgresql.c \
|
||||||
regex.c \
|
regex.c \
|
||||||
spp_ai.c \
|
spp_ai.c \
|
||||||
stream.c
|
stream.c \
|
||||||
|
webserv.c
|
||||||
|
|
||||||
ACLOCAL_AMFLAGS = -I m4
|
ACLOCAL_AMFLAGS = -I m4
|
||||||
EXTRA_DIST = README INSTALL ChangeLog AUTHORS COPYING Doxyfile NEWS TODO doc etc include uthash corr_rules *.h
|
EXTRA_DIST = README INSTALL ChangeLog AUTHORS COPYING Doxyfile NEWS TODO doc etc include uthash corr_rules *.h
|
||||||
|
@ -411,6 +416,9 @@ libsf_ai_preproc_la-spp_ai.lo: spp_ai.c
|
||||||
libsf_ai_preproc_la-stream.lo: stream.c
|
libsf_ai_preproc_la-stream.lo: stream.c
|
||||||
$(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_ai_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_ai_preproc_la-stream.lo `test -f 'stream.c' || echo '$(srcdir)/'`stream.c
|
$(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_ai_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_ai_preproc_la-stream.lo `test -f 'stream.c' || echo '$(srcdir)/'`stream.c
|
||||||
|
|
||||||
|
libsf_ai_preproc_la-webserv.lo: webserv.c
|
||||||
|
$(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_ai_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_ai_preproc_la-webserv.lo `test -f 'webserv.c' || echo '$(srcdir)/'`webserv.c
|
||||||
|
|
||||||
libsf_ai_preproc_la-sf_dynamic_preproc_lib.lo: include/sf_dynamic_preproc_lib.c
|
libsf_ai_preproc_la-sf_dynamic_preproc_lib.lo: include/sf_dynamic_preproc_lib.c
|
||||||
$(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_ai_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_ai_preproc_la-sf_dynamic_preproc_lib.lo `test -f 'include/sf_dynamic_preproc_lib.c' || echo '$(srcdir)/'`include/sf_dynamic_preproc_lib.c
|
$(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_ai_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_ai_preproc_la-sf_dynamic_preproc_lib.lo `test -f 'include/sf_dynamic_preproc_lib.c' || echo '$(srcdir)/'`include/sf_dynamic_preproc_lib.c
|
||||||
|
|
||||||
|
|
4
TODO
4
TODO
|
@ -2,8 +2,9 @@
|
||||||
AVERAGE/HIGH PRIORITY:
|
AVERAGE/HIGH PRIORITY:
|
||||||
======================
|
======================
|
||||||
|
|
||||||
- Full PostgreSQL support for output db
|
|
||||||
- Web interface
|
- Web interface
|
||||||
|
- Bayesian network
|
||||||
|
- Modules for correlation coefficients
|
||||||
- Code profiling
|
- Code profiling
|
||||||
- Comment all the code!!!
|
- Comment all the code!!!
|
||||||
- Saving packet flows as .pcap
|
- Saving packet flows as .pcap
|
||||||
|
@ -33,4 +34,5 @@ DONE:
|
||||||
+ Clustering alerts with time constraints
|
+ Clustering alerts with time constraints
|
||||||
+ Save clusters and correlations to db
|
+ Save clusters and correlations to db
|
||||||
+ Uniformed error messages format
|
+ Uniformed error messages format
|
||||||
|
+ Full PostgreSQL support for output db
|
||||||
|
|
||||||
|
|
7971
aclocal.m4
vendored
7971
aclocal.m4
vendored
File diff suppressed because it is too large
Load diff
15
config.h.in
15
config.h.in
|
@ -20,6 +20,9 @@
|
||||||
/* Define if FreeBSD */
|
/* Define if FreeBSD */
|
||||||
#undef FREEBSD
|
#undef FREEBSD
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `accept' function. */
|
||||||
|
#undef HAVE_ACCEPT
|
||||||
|
|
||||||
/* Define to 1 if you have the `alarm' function. */
|
/* Define to 1 if you have the `alarm' function. */
|
||||||
#undef HAVE_ALARM
|
#undef HAVE_ALARM
|
||||||
|
|
||||||
|
@ -30,9 +33,15 @@
|
||||||
*/
|
*/
|
||||||
#undef HAVE_ALLOCA_H
|
#undef HAVE_ALLOCA_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `bind' function. */
|
||||||
|
#undef HAVE_BIND
|
||||||
|
|
||||||
/* Define to 1 if the system has the type `boolean'. */
|
/* Define to 1 if the system has the type `boolean'. */
|
||||||
#undef HAVE_BOOLEAN
|
#undef HAVE_BOOLEAN
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `connect' function. */
|
||||||
|
#undef HAVE_CONNECT
|
||||||
|
|
||||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||||
#undef HAVE_DLFCN_H
|
#undef HAVE_DLFCN_H
|
||||||
|
|
||||||
|
@ -72,6 +81,9 @@
|
||||||
/* Define to 1 if you have the <limits.h> header file. */
|
/* Define to 1 if you have the <limits.h> header file. */
|
||||||
#undef HAVE_LIMITS_H
|
#undef HAVE_LIMITS_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `listen' function. */
|
||||||
|
#undef HAVE_LISTEN
|
||||||
|
|
||||||
/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
|
/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
|
||||||
to 0 otherwise. */
|
to 0 otherwise. */
|
||||||
#undef HAVE_MALLOC
|
#undef HAVE_MALLOC
|
||||||
|
@ -98,6 +110,9 @@
|
||||||
/* Define to 1 if you have the `regcomp' function. */
|
/* Define to 1 if you have the `regcomp' function. */
|
||||||
#undef HAVE_REGCOMP
|
#undef HAVE_REGCOMP
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `socket' function. */
|
||||||
|
#undef HAVE_SOCKET
|
||||||
|
|
||||||
/* Define to 1 if stdbool.h conforms to C99. */
|
/* Define to 1 if stdbool.h conforms to C99. */
|
||||||
#undef HAVE_STDBOOL_H
|
#undef HAVE_STDBOOL_H
|
||||||
|
|
||||||
|
|
26
configure
vendored
26
configure
vendored
|
@ -7305,6 +7305,10 @@ _lt_linker_boilerplate=`cat conftest.err`
|
||||||
$RM -r conftest*
|
$RM -r conftest*
|
||||||
|
|
||||||
|
|
||||||
|
## CAVEAT EMPTOR:
|
||||||
|
## There is no encapsulation within the following macros, do not change
|
||||||
|
## the running order or otherwise move them around unless you know exactly
|
||||||
|
## what you are doing...
|
||||||
if test -n "$compiler"; then
|
if test -n "$compiler"; then
|
||||||
|
|
||||||
lt_prog_compiler_no_builtin_flag=
|
lt_prog_compiler_no_builtin_flag=
|
||||||
|
@ -7330,11 +7334,11 @@ else
|
||||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||||
-e 's:$: $lt_compiler_flag:'`
|
-e 's:$: $lt_compiler_flag:'`
|
||||||
(eval echo "\"\$as_me:7333: $lt_compile\"" >&5)
|
(eval echo "\"\$as_me:7337: $lt_compile\"" >&5)
|
||||||
(eval "$lt_compile" 2>conftest.err)
|
(eval "$lt_compile" 2>conftest.err)
|
||||||
ac_status=$?
|
ac_status=$?
|
||||||
cat conftest.err >&5
|
cat conftest.err >&5
|
||||||
echo "$as_me:7337: \$? = $ac_status" >&5
|
echo "$as_me:7341: \$? = $ac_status" >&5
|
||||||
if (exit $ac_status) && test -s "$ac_outfile"; then
|
if (exit $ac_status) && test -s "$ac_outfile"; then
|
||||||
# The compiler can only warn and ignore the option if not recognized
|
# The compiler can only warn and ignore the option if not recognized
|
||||||
# So say no if there are warnings other than the usual output.
|
# So say no if there are warnings other than the usual output.
|
||||||
|
@ -7669,11 +7673,11 @@ else
|
||||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||||
-e 's:$: $lt_compiler_flag:'`
|
-e 's:$: $lt_compiler_flag:'`
|
||||||
(eval echo "\"\$as_me:7672: $lt_compile\"" >&5)
|
(eval echo "\"\$as_me:7676: $lt_compile\"" >&5)
|
||||||
(eval "$lt_compile" 2>conftest.err)
|
(eval "$lt_compile" 2>conftest.err)
|
||||||
ac_status=$?
|
ac_status=$?
|
||||||
cat conftest.err >&5
|
cat conftest.err >&5
|
||||||
echo "$as_me:7676: \$? = $ac_status" >&5
|
echo "$as_me:7680: \$? = $ac_status" >&5
|
||||||
if (exit $ac_status) && test -s "$ac_outfile"; then
|
if (exit $ac_status) && test -s "$ac_outfile"; then
|
||||||
# The compiler can only warn and ignore the option if not recognized
|
# The compiler can only warn and ignore the option if not recognized
|
||||||
# So say no if there are warnings other than the usual output.
|
# So say no if there are warnings other than the usual output.
|
||||||
|
@ -7774,11 +7778,11 @@ else
|
||||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||||
-e 's:$: $lt_compiler_flag:'`
|
-e 's:$: $lt_compiler_flag:'`
|
||||||
(eval echo "\"\$as_me:7777: $lt_compile\"" >&5)
|
(eval echo "\"\$as_me:7781: $lt_compile\"" >&5)
|
||||||
(eval "$lt_compile" 2>out/conftest.err)
|
(eval "$lt_compile" 2>out/conftest.err)
|
||||||
ac_status=$?
|
ac_status=$?
|
||||||
cat out/conftest.err >&5
|
cat out/conftest.err >&5
|
||||||
echo "$as_me:7781: \$? = $ac_status" >&5
|
echo "$as_me:7785: \$? = $ac_status" >&5
|
||||||
if (exit $ac_status) && test -s out/conftest2.$ac_objext
|
if (exit $ac_status) && test -s out/conftest2.$ac_objext
|
||||||
then
|
then
|
||||||
# The compiler can only warn and ignore the option if not recognized
|
# The compiler can only warn and ignore the option if not recognized
|
||||||
|
@ -7829,11 +7833,11 @@ else
|
||||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||||
-e 's:$: $lt_compiler_flag:'`
|
-e 's:$: $lt_compiler_flag:'`
|
||||||
(eval echo "\"\$as_me:7832: $lt_compile\"" >&5)
|
(eval echo "\"\$as_me:7836: $lt_compile\"" >&5)
|
||||||
(eval "$lt_compile" 2>out/conftest.err)
|
(eval "$lt_compile" 2>out/conftest.err)
|
||||||
ac_status=$?
|
ac_status=$?
|
||||||
cat out/conftest.err >&5
|
cat out/conftest.err >&5
|
||||||
echo "$as_me:7836: \$? = $ac_status" >&5
|
echo "$as_me:7840: \$? = $ac_status" >&5
|
||||||
if (exit $ac_status) && test -s out/conftest2.$ac_objext
|
if (exit $ac_status) && test -s out/conftest2.$ac_objext
|
||||||
then
|
then
|
||||||
# The compiler can only warn and ignore the option if not recognized
|
# The compiler can only warn and ignore the option if not recognized
|
||||||
|
@ -10213,7 +10217,7 @@ else
|
||||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||||
lt_status=$lt_dlunknown
|
lt_status=$lt_dlunknown
|
||||||
cat > conftest.$ac_ext <<_LT_EOF
|
cat > conftest.$ac_ext <<_LT_EOF
|
||||||
#line 10216 "configure"
|
#line 10220 "configure"
|
||||||
#include "confdefs.h"
|
#include "confdefs.h"
|
||||||
|
|
||||||
#if HAVE_DLFCN_H
|
#if HAVE_DLFCN_H
|
||||||
|
@ -10309,7 +10313,7 @@ else
|
||||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||||
lt_status=$lt_dlunknown
|
lt_status=$lt_dlunknown
|
||||||
cat > conftest.$ac_ext <<_LT_EOF
|
cat > conftest.$ac_ext <<_LT_EOF
|
||||||
#line 10312 "configure"
|
#line 10316 "configure"
|
||||||
#include "confdefs.h"
|
#include "confdefs.h"
|
||||||
|
|
||||||
#if HAVE_DLFCN_H
|
#if HAVE_DLFCN_H
|
||||||
|
@ -12695,7 +12699,7 @@ $as_echo "#define realloc rpl_realloc" >>confdefs.h
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
for ac_func in memmove memset regcomp strcasecmp strdup strstr strtol strtoul
|
for ac_func in memmove memset regcomp strcasecmp strdup strstr strtol strtoul socket bind listen accept connect
|
||||||
do :
|
do :
|
||||||
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
|
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
|
||||||
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
|
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
|
||||||
|
|
|
@ -179,7 +179,7 @@ AC_CHECK_TYPES([ptrdiff_t])
|
||||||
AC_FUNC_MALLOC
|
AC_FUNC_MALLOC
|
||||||
AC_FUNC_MKTIME
|
AC_FUNC_MKTIME
|
||||||
AC_FUNC_REALLOC
|
AC_FUNC_REALLOC
|
||||||
AC_CHECK_FUNCS([memmove memset regcomp strcasecmp strdup strstr strtol strtoul],,AC_MSG_ERROR(At least one of the required functions was not found))
|
AC_CHECK_FUNCS([memmove memset regcomp strcasecmp strdup strstr strtol strtoul socket bind listen accept connect],,AC_MSG_ERROR(At least one of the required functions was not found))
|
||||||
|
|
||||||
AC_DEFINE([VERSION], ["0.1.0"], [Module version])
|
AC_DEFINE([VERSION], ["0.1.0"], [Module version])
|
||||||
AC_DEFINE([PACKAGE], ["sf_ai_preprocessor"], [Package name])
|
AC_DEFINE([PACKAGE], ["sf_ai_preprocessor"], [Package name])
|
||||||
|
|
18
regex.c
18
regex.c
|
@ -17,6 +17,7 @@
|
||||||
* =====================================================================================
|
* =====================================================================================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "spp_ai.h"
|
||||||
#include "uthash.h"
|
#include "uthash.h"
|
||||||
|
|
||||||
#include <alloca.h>
|
#include <alloca.h>
|
||||||
|
@ -25,6 +26,9 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
/** \defgroup regex Regex management
|
||||||
|
* @{ */
|
||||||
|
|
||||||
/** Compiled and cached regular expression entry */
|
/** Compiled and cached regular expression entry */
|
||||||
struct regex_cache_entry {
|
struct regex_cache_entry {
|
||||||
/** The expression itself, used as the key of the hashtable */
|
/** The expression itself, used as the key of the hashtable */
|
||||||
|
@ -34,15 +38,13 @@ struct regex_cache_entry {
|
||||||
/** Make the struct 'hashable' */
|
/** Make the struct 'hashable' */
|
||||||
UT_hash_handle hh;
|
UT_hash_handle hh;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Regular expression cache container
|
* Regular expression cache container
|
||||||
* TODO: Free the cache at the end of program execution.
|
* TODO: Free the cache at the end of program execution.
|
||||||
*/
|
*/
|
||||||
static struct regex_cache_entry *reg_cache = NULL;
|
static struct regex_cache_entry *reg_cache = NULL;
|
||||||
|
|
||||||
/** \defgroup regex Regex management
|
|
||||||
* @{ */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Check if a string matches a regular expression
|
* \brief Check if a string matches a regular expression
|
||||||
* \param expr Regular expression to be matched
|
* \param expr Regular expression to be matched
|
||||||
|
@ -67,13 +69,13 @@ preg_match ( const char* expr, char* str, char*** matches, int *nmatches )
|
||||||
* Search for a compiled regex in the cache.
|
* Search for a compiled regex in the cache.
|
||||||
*/
|
*/
|
||||||
HASH_FIND_STR( reg_cache, expr, cached_regex );
|
HASH_FIND_STR( reg_cache, expr, cached_regex );
|
||||||
|
|
||||||
if( cached_regex != NULL ){
|
if( cached_regex != NULL ){
|
||||||
/*
|
/*
|
||||||
* Yeppa!
|
* Yeppa!
|
||||||
*/
|
*/
|
||||||
regex = cached_regex->compiled;
|
regex = cached_regex->compiled;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
/*
|
/*
|
||||||
* Not found, create a new structure, compile the regexp and add it to the cache
|
* Not found, create a new structure, compile the regexp and add it to the cache
|
||||||
* for latter use.
|
* for latter use.
|
||||||
|
@ -154,6 +156,7 @@ preg_match ( const char* expr, char* str, char*** matches, int *nmatches )
|
||||||
* \param rep Replacement for 'orig'
|
* \param rep Replacement for 'orig'
|
||||||
* \return The string with the replacement
|
* \return The string with the replacement
|
||||||
*/
|
*/
|
||||||
|
|
||||||
char*
|
char*
|
||||||
str_replace ( char *str, char *orig, char *rep )
|
str_replace ( char *str, char *orig, char *rep )
|
||||||
{
|
{
|
||||||
|
@ -164,7 +167,7 @@ str_replace ( char *str, char *orig, char *rep )
|
||||||
if ( !( pos = (int) strstr ( str, orig )))
|
if ( !( pos = (int) strstr ( str, orig )))
|
||||||
return str;
|
return str;
|
||||||
|
|
||||||
new_len = strlen(str) - strlen(orig) + strlen(rep) + 1;
|
new_len = strlen(str) - strlen(orig) + ((rep) ? strlen(rep) : 0) + 1;
|
||||||
|
|
||||||
if ( !( new_s = (char*) malloc ( new_len )))
|
if ( !( new_s = (char*) malloc ( new_len )))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -193,6 +196,7 @@ str_replace ( char *str, char *orig, char *rep )
|
||||||
* \param rep Replacement for 'orig'
|
* \param rep Replacement for 'orig'
|
||||||
* \return The string with the replacement
|
* \return The string with the replacement
|
||||||
*/
|
*/
|
||||||
|
|
||||||
char*
|
char*
|
||||||
str_replace_all ( char *str, char *orig, char *rep )
|
str_replace_all ( char *str, char *orig, char *rep )
|
||||||
{
|
{
|
||||||
|
@ -205,7 +209,7 @@ str_replace_all ( char *str, char *orig, char *rep )
|
||||||
free ( tmp );
|
free ( tmp );
|
||||||
|
|
||||||
tmp = buf;
|
tmp = buf;
|
||||||
buf = str_replace ( str, orig, rep );
|
buf = str_replace ( buf, orig, rep );
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
|
|
136
spp_ai.c
136
spp_ai.c
|
@ -91,6 +91,7 @@ static void AI_init(char *args)
|
||||||
{
|
{
|
||||||
pthread_t cleanup_thread,
|
pthread_t cleanup_thread,
|
||||||
logparse_thread,
|
logparse_thread,
|
||||||
|
webserv_thread,
|
||||||
correlation_thread;
|
correlation_thread;
|
||||||
|
|
||||||
tSfPolicyId policy_id = _dpd.getParserPolicy();
|
tSfPolicyId policy_id = _dpd.getParserPolicy();
|
||||||
|
@ -136,6 +137,15 @@ static void AI_init(char *args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If webserv_port is != 0, start the web server */
|
||||||
|
if ( config->webserv_port != 0 )
|
||||||
|
{
|
||||||
|
if ( pthread_create ( &webserv_thread, NULL, AI_webserv_thread, NULL ) != 0 )
|
||||||
|
{
|
||||||
|
AI_fatal_err ( "Failed to create the web server thread", __FILE__, __LINE__ );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Register the preprocessor function, Transport layer, ID 10000 */
|
/* Register the preprocessor function, Transport layer, ID 10000 */
|
||||||
_dpd.addPreproc(AI_process, PRIORITY_TRANSPORT, 10000, PROTO_BIT__TCP | PROTO_BIT__UDP);
|
_dpd.addPreproc(AI_process, PRIORITY_TRANSPORT, 10000, PROTO_BIT__TCP | PROTO_BIT__UDP);
|
||||||
DEBUG_WRAP(_dpd.debugMsg(DEBUG_PLUGIN, "Preprocessor: AI is initialized\n"););
|
DEBUG_WRAP(_dpd.debugMsg(DEBUG_PLUGIN, "Preprocessor: AI is initialized\n"););
|
||||||
|
@ -151,11 +161,13 @@ static AI_config * AI_parse(char *args)
|
||||||
{
|
{
|
||||||
char *arg;
|
char *arg;
|
||||||
char *match;
|
char *match;
|
||||||
char alertfile[1024] = { 0 };
|
char alertfile[1024] = { 0 },
|
||||||
char clusterfile[1024] = { 0 };
|
clusterfile[1024] = { 0 },
|
||||||
char corr_rules_dir[1024] = { 0 };
|
corr_rules_dir[1024] = { 0 },
|
||||||
char corr_alerts_dir[1024] = { 0 };
|
corr_alerts_dir[1024] = { 0 },
|
||||||
char alert_history_file[1024] = { 0 };
|
alert_history_file[1024] = { 0 },
|
||||||
|
webserv_dir[1024] = { 0 },
|
||||||
|
webserv_banner[1024] = { 0 };
|
||||||
|
|
||||||
char **matches = NULL;
|
char **matches = NULL;
|
||||||
int nmatches = 0;
|
int nmatches = 0;
|
||||||
|
@ -174,6 +186,7 @@ static AI_config * AI_parse(char *args)
|
||||||
hierarchy_node **hierarchy_nodes = NULL;
|
hierarchy_node **hierarchy_nodes = NULL;
|
||||||
int n_hierarchy_nodes = 0;
|
int n_hierarchy_nodes = 0;
|
||||||
|
|
||||||
|
unsigned short webserv_port = 0;
|
||||||
unsigned long cleanup_interval = 0,
|
unsigned long cleanup_interval = 0,
|
||||||
stream_expire_interval = 0,
|
stream_expire_interval = 0,
|
||||||
alertfile_len = 0,
|
alertfile_len = 0,
|
||||||
|
@ -186,6 +199,8 @@ static AI_config * AI_parse(char *args)
|
||||||
cluster_max_alert_interval = 0,
|
cluster_max_alert_interval = 0,
|
||||||
corr_rules_dir_len = 0,
|
corr_rules_dir_len = 0,
|
||||||
corr_alerts_dir_len = 0,
|
corr_alerts_dir_len = 0,
|
||||||
|
webserv_dir_len = 0,
|
||||||
|
webserv_banner_len = 0,
|
||||||
alert_clustering_interval = 0,
|
alert_clustering_interval = 0,
|
||||||
database_parsing_interval = 0,
|
database_parsing_interval = 0,
|
||||||
correlation_graph_interval = 0;
|
correlation_graph_interval = 0;
|
||||||
|
@ -195,6 +210,8 @@ static AI_config * AI_parse(char *args)
|
||||||
has_correlation_interval = false,
|
has_correlation_interval = false,
|
||||||
has_corr_alerts_dir = false,
|
has_corr_alerts_dir = false,
|
||||||
has_database_interval = false,
|
has_database_interval = false,
|
||||||
|
has_webserv_dir = false,
|
||||||
|
has_webserv_banner = false,
|
||||||
has_alertfile = false,
|
has_alertfile = false,
|
||||||
has_clusterfile = false,
|
has_clusterfile = false,
|
||||||
has_corr_rules_dir = false,
|
has_corr_rules_dir = false,
|
||||||
|
@ -341,11 +358,30 @@ static AI_config * AI_parse(char *args)
|
||||||
_dpd.logMsg(" Alert buffer size: %d\n", config->alert_bufsize );
|
_dpd.logMsg(" Alert buffer size: %d\n", config->alert_bufsize );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Parsing the webserv_port option */
|
||||||
|
if (( arg = (char*) strcasestr( args, "webserv_port" ) ))
|
||||||
|
{
|
||||||
|
for ( arg += strlen("webserv_port");
|
||||||
|
*arg && (*arg < '0' || *arg > '9');
|
||||||
|
arg++ );
|
||||||
|
|
||||||
|
if ( !(*arg) )
|
||||||
|
{
|
||||||
|
AI_fatal_err( "webserv_port option used but "
|
||||||
|
"no value specified", __FILE__, __LINE__ );
|
||||||
|
}
|
||||||
|
|
||||||
|
webserv_port = (unsigned short) strtoul(arg, NULL, 10);
|
||||||
|
config->webserv_port= webserv_port;
|
||||||
|
} else {
|
||||||
|
config->webserv_port = DEFAULT_WEBSERV_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
_dpd.logMsg(" Web server port: %d\n", config->webserv_port );
|
||||||
|
|
||||||
/* Parsing the correlation_threshold_coefficient option */
|
/* Parsing the correlation_threshold_coefficient option */
|
||||||
if (( arg = (char*) strcasestr( args, "correlation_threshold_coefficient" ) ))
|
if (( arg = (char*) strcasestr( args, "correlation_threshold_coefficient" ) ))
|
||||||
{
|
{
|
||||||
/* has_stream_expire_interval = true; */
|
|
||||||
|
|
||||||
for ( arg += strlen("correlation_threshold_coefficient");
|
for ( arg += strlen("correlation_threshold_coefficient");
|
||||||
*arg && (*arg < '0' || *arg > '9');
|
*arg && (*arg < '0' || *arg > '9');
|
||||||
arg++ );
|
arg++ );
|
||||||
|
@ -357,10 +393,10 @@ static AI_config * AI_parse(char *args)
|
||||||
}
|
}
|
||||||
|
|
||||||
corr_threshold_coefficient = strtod ( arg, NULL );
|
corr_threshold_coefficient = strtod ( arg, NULL );
|
||||||
_dpd.logMsg( " Correlation threshold coefficient: %f\n", corr_threshold_coefficient );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config->correlationThresholdCoefficient = corr_threshold_coefficient;
|
config->correlationThresholdCoefficient = corr_threshold_coefficient;
|
||||||
|
_dpd.logMsg( " Correlation threshold coefficient: %f\n", corr_threshold_coefficient );
|
||||||
|
|
||||||
/* Parsing the bayesian_correlation_interval option */
|
/* Parsing the bayesian_correlation_interval option */
|
||||||
if (( arg = (char*) strcasestr( args, "bayesian_correlation_interval" ) ))
|
if (( arg = (char*) strcasestr( args, "bayesian_correlation_interval" ) ))
|
||||||
|
@ -589,6 +625,90 @@ static AI_config * AI_parse(char *args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Parsing the webserv_dir option */
|
||||||
|
if (( arg = (char*) strcasestr( args, "webserv_dir" ) ))
|
||||||
|
{
|
||||||
|
for ( arg += strlen("webserv_dir");
|
||||||
|
*arg && *arg != '"';
|
||||||
|
arg++ );
|
||||||
|
|
||||||
|
if ( !(*(arg++)) )
|
||||||
|
{
|
||||||
|
AI_fatal_err ( "webserv_dir option used but no filename specified", __FILE__, __LINE__ );
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( webserv_dir[ (++webserv_dir_len)-1 ] = *arg;
|
||||||
|
*arg && *arg != '"' && webserv_dir_len < sizeof ( webserv_dir );
|
||||||
|
arg++, webserv_dir[ (++webserv_dir_len)-1 ] = *arg );
|
||||||
|
|
||||||
|
if ( webserv_dir[0] == 0 || webserv_dir_len <= 1 ) {
|
||||||
|
has_webserv_dir = false;
|
||||||
|
} else {
|
||||||
|
if ( webserv_dir_len >= sizeof ( webserv_dir )) {
|
||||||
|
AI_fatal_err ( "webserv_dir path too long ( >= 1024 )", __FILE__, __LINE__ );
|
||||||
|
} else if ( strlen( webserv_dir ) == 0 ) {
|
||||||
|
has_webserv_dir = false;
|
||||||
|
} else {
|
||||||
|
has_webserv_dir = true;
|
||||||
|
webserv_dir[ webserv_dir_len-1 ] = 0;
|
||||||
|
strncpy ( config->webserv_dir, webserv_dir, webserv_dir_len );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! has_webserv_dir )
|
||||||
|
{
|
||||||
|
#ifndef HAVE_CONFIG_H
|
||||||
|
AI_fatal_err ( "Unable to read PREFIX from config.h", __FILE__, __LINE__ );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
snprintf ( config->webserv_dir, sizeof ( config->webserv_dir ), "%s/share/snort_ai_preprocessor/htdocs", PREFIX );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove unnecessary '/' at the end of the web server directory */
|
||||||
|
for ( i = strlen ( config->webserv_dir ) - 1; i >= 0 && config->webserv_dir[i] == '/'; i-- )
|
||||||
|
config->webserv_dir[i] = 0;
|
||||||
|
|
||||||
|
_dpd.logMsg(" webserv_dir: %s\n", config->webserv_dir);
|
||||||
|
|
||||||
|
/* Parsing the webserv_banner option */
|
||||||
|
if (( arg = (char*) strcasestr( args, "webserv_banner" ) ))
|
||||||
|
{
|
||||||
|
for ( arg += strlen("webserv_banner");
|
||||||
|
*arg && *arg != '"';
|
||||||
|
arg++ );
|
||||||
|
|
||||||
|
if ( !(*(arg++)) )
|
||||||
|
{
|
||||||
|
AI_fatal_err ( "webserv_banner option used but no value specified", __FILE__, __LINE__ );
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( webserv_banner[ (++webserv_banner_len)-1 ] = *arg;
|
||||||
|
*arg && *arg != '"' && webserv_banner_len < sizeof ( webserv_banner );
|
||||||
|
arg++, webserv_banner[ (++webserv_banner_len)-1 ] = *arg );
|
||||||
|
|
||||||
|
if ( webserv_banner[0] == 0 || webserv_banner_len <= 1 ) {
|
||||||
|
has_webserv_banner = false;
|
||||||
|
} else {
|
||||||
|
if ( webserv_banner_len >= sizeof ( webserv_banner )) {
|
||||||
|
AI_fatal_err ( "webserv_banner path too long ( >= 1024 )", __FILE__, __LINE__ );
|
||||||
|
} else if ( strlen( webserv_banner ) == 0 ) {
|
||||||
|
has_webserv_banner = false;
|
||||||
|
} else {
|
||||||
|
has_webserv_banner = true;
|
||||||
|
webserv_banner[ webserv_banner_len-1 ] = 0;
|
||||||
|
strncpy ( config->webserv_banner, webserv_banner, webserv_banner_len );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! has_webserv_banner )
|
||||||
|
{
|
||||||
|
strncpy ( config->webserv_banner, DEFAULT_WEBSERV_BANNER, webserv_banner_len );
|
||||||
|
}
|
||||||
|
|
||||||
|
_dpd.logMsg(" webserv_banner: %s\n", config->webserv_banner);
|
||||||
|
|
||||||
/* Parsing database option */
|
/* Parsing database option */
|
||||||
if ( preg_match ( "\\s+database\\s*\\(\\s*([^\\)]+)\\)", args, &matches, &nmatches ) > 0 )
|
if ( preg_match ( "\\s+database\\s*\\(\\s*([^\\)]+)\\)", args, &matches, &nmatches ) > 0 )
|
||||||
{
|
{
|
||||||
|
|
18
spp_ai.h
18
spp_ai.h
|
@ -78,6 +78,12 @@
|
||||||
/** Default maximum interval, in seconds, between two alerts for being considered in the same cluster */
|
/** Default maximum interval, in seconds, between two alerts for being considered in the same cluster */
|
||||||
#define DEFAULT_CLUSTER_MAX_ALERT_INTERVAL 14400
|
#define DEFAULT_CLUSTER_MAX_ALERT_INTERVAL 14400
|
||||||
|
|
||||||
|
/** Default web server port */
|
||||||
|
#define DEFAULT_WEBSERV_PORT 7654
|
||||||
|
|
||||||
|
/** Default web server banner */
|
||||||
|
#define DEFAULT_WEBSERV_BANNER "Snort AIPreprocessor module"
|
||||||
|
|
||||||
/** Cutoff y value in the exponential decay for considering two alerts not correlated */
|
/** Cutoff y value in the exponential decay for considering two alerts not correlated */
|
||||||
#define CUTOFF_Y_VALUE 0.01
|
#define CUTOFF_Y_VALUE 0.01
|
||||||
|
|
||||||
|
@ -177,6 +183,17 @@ typedef struct
|
||||||
* may occur at all! */
|
* may occur at all! */
|
||||||
double correlationThresholdCoefficient;
|
double correlationThresholdCoefficient;
|
||||||
|
|
||||||
|
/** Port where the webserver providing the web interface for the correlation graph
|
||||||
|
* will listen onto */
|
||||||
|
unsigned short webserv_port;
|
||||||
|
|
||||||
|
/** (Absolute) path to the directory containing the HTML files for the web interface */
|
||||||
|
char webserv_dir[1024];
|
||||||
|
|
||||||
|
/** Banner string of the web server (this will be placed in the 'Server' HTTP header
|
||||||
|
* and in the footer of error pages */
|
||||||
|
char webserv_banner[1024];
|
||||||
|
|
||||||
/** Alert file */
|
/** Alert file */
|
||||||
char alertfile[1024];
|
char alertfile[1024];
|
||||||
|
|
||||||
|
@ -396,6 +413,7 @@ void AI_fatal_err ( const char *msg, const char *file, const int l
|
||||||
void* AI_hashcleanup_thread ( void* );
|
void* AI_hashcleanup_thread ( void* );
|
||||||
void* AI_file_alertparser_thread ( void* );
|
void* AI_file_alertparser_thread ( void* );
|
||||||
void* AI_alert_correlation_thread ( void* );
|
void* AI_alert_correlation_thread ( void* );
|
||||||
|
void* AI_webserv_thread ( void* );
|
||||||
|
|
||||||
#ifdef HAVE_DB
|
#ifdef HAVE_DB
|
||||||
AI_snort_alert* AI_db_get_alerts ( void );
|
AI_snort_alert* AI_db_get_alerts ( void );
|
||||||
|
|
460
webserv.c
Normal file
460
webserv.c
Normal file
|
@ -0,0 +1,460 @@
|
||||||
|
/*
|
||||||
|
* =====================================================================================
|
||||||
|
*
|
||||||
|
* Filename: webserv.c
|
||||||
|
*
|
||||||
|
* Description: Web server for managing the web interface of the module
|
||||||
|
*
|
||||||
|
* Version: 0.1
|
||||||
|
* Created: 05/10/2010 14:12:24
|
||||||
|
* Revision: none
|
||||||
|
* Compiler: gcc
|
||||||
|
*
|
||||||
|
* Author: BlackLight (http://0x00.ath.cx), <blacklight@autistici.org>
|
||||||
|
* Licence: GNU GPL v.3
|
||||||
|
* Company: DO WHAT YOU WANT CAUSE A PIRATE IS FREE, YOU ARE A PIRATE!
|
||||||
|
*
|
||||||
|
* =====================================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "spp_ai.h"
|
||||||
|
|
||||||
|
#include <alloca.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
/** \defgroup webserv Web server managing the web interface of the module
|
||||||
|
* @{ */
|
||||||
|
|
||||||
|
#define HTTP_RESPONSE_HEADERS_FORMAT "%s %d %s\r\n" \
|
||||||
|
"Date: %s\r\n" \
|
||||||
|
"Server: %s\r\n" \
|
||||||
|
"Content-Type: %s\r\n" \
|
||||||
|
"Content-Length: %u\r\n\r\n"
|
||||||
|
|
||||||
|
#define HTTP_ERR_RESPONSE_FORMAT "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n" \
|
||||||
|
"<html><head>\n" \
|
||||||
|
"<title>%u %s</title>\n" \
|
||||||
|
"</head><body>\n" \
|
||||||
|
"<h1>%s</h1>\n" \
|
||||||
|
"<p>%s</p>\n" \
|
||||||
|
"<hr>\n" \
|
||||||
|
"<i>%s</i>\n" \
|
||||||
|
"</body></html>\n\n"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Escape a string to be used or included in an URL
|
||||||
|
* \param str String to be escaped
|
||||||
|
* \param out Pointer to the string containing the output (its length must be at least 3*length(str) + 2)
|
||||||
|
* \param str_len Length of the input string
|
||||||
|
* \return The length of the output string after the operation
|
||||||
|
*/
|
||||||
|
|
||||||
|
size_t
|
||||||
|
__AI_url_escape ( char *str, char **out, size_t str_len )
|
||||||
|
{
|
||||||
|
char escape_seq[5] = { 0 };
|
||||||
|
size_t i,
|
||||||
|
out_len = 0;
|
||||||
|
|
||||||
|
for ( i=0; i < str_len; i++ )
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
( str[i] >= 'a' && str[i] <= 'z' ) ||
|
||||||
|
( str[i] >= 'A' && str[i] <= 'Z' ) ||
|
||||||
|
( str[i] >= '0' && str[i] <= '9' ) ||
|
||||||
|
str[i] == '_' || str[i] == '.' || str[i] == '/' ||
|
||||||
|
str[i] == '?' || str[i] == '=' )
|
||||||
|
{
|
||||||
|
(*out)[out_len++] = str[i];
|
||||||
|
} else {
|
||||||
|
snprintf ( escape_seq, sizeof ( escape_seq ), "%%%.2X", str[i] );
|
||||||
|
(*out)[out_len++] = escape_seq[0];
|
||||||
|
(*out)[out_len++] = escape_seq[1];
|
||||||
|
(*out)[out_len++] = escape_seq[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(*out)[out_len] = 0;
|
||||||
|
return out_len;
|
||||||
|
} /* ----- end of function __AI_url_escape ----- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Unescape a string in URL format
|
||||||
|
* \param str String to be unescaped
|
||||||
|
* \param out Pointer to the string containing the output (its length must be at least the same of str + 1)
|
||||||
|
* \param str_len Length of the input string
|
||||||
|
* \return The length of the output string after the operation
|
||||||
|
*/
|
||||||
|
|
||||||
|
PRIVATE size_t
|
||||||
|
__AI_url_unescape ( char *str, char **out, size_t str_len )
|
||||||
|
{
|
||||||
|
char escape_seq[5] = { 0 };
|
||||||
|
size_t i,
|
||||||
|
out_len = 0;
|
||||||
|
|
||||||
|
for ( i=0; i < str_len; i++ )
|
||||||
|
{
|
||||||
|
if ( str[i] == '%' && i < str_len-2 )
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
(( str[i+1] >= '0' && str[i+1] <= '9' ) ||
|
||||||
|
( str[i+1] >= 'a' && str[i+1] <= 'z' ) ||
|
||||||
|
( str[i+1] >= 'A' && str[i+1] <= 'Z' )) &&
|
||||||
|
(( str[i+2] >= '0' && str[i+2] <= '9' ) ||
|
||||||
|
( str[i+2] >= 'a' && str[i+2] <= 'z' ) ||
|
||||||
|
( str[i+2] >= 'A' && str[i+2] <= 'Z' )))
|
||||||
|
{
|
||||||
|
escape_seq[0] = str[i+1];
|
||||||
|
escape_seq[1] = str[i+2];
|
||||||
|
escape_seq[2] = 0;
|
||||||
|
(*out)[out_len++] = (char) strtoul ( escape_seq, NULL, 16 );
|
||||||
|
i += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(*out)[out_len++] = str[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
(*out)[out_len] = 0;
|
||||||
|
return out_len;
|
||||||
|
} /* ----- end of function __AI_url_unescape ----- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Read a line from a file descriptor
|
||||||
|
* \param fp FILE descriptor
|
||||||
|
* \return A char* pointer to the read line (to be free-ed!!!)
|
||||||
|
*/
|
||||||
|
|
||||||
|
PRIVATE char*
|
||||||
|
__AI_getline ( FILE *fp )
|
||||||
|
{
|
||||||
|
char ch;
|
||||||
|
char *line = NULL;
|
||||||
|
int nbytes = 1;
|
||||||
|
|
||||||
|
if ( !feof ( fp ))
|
||||||
|
{
|
||||||
|
while (( ch = fgetc ( fp )) != '\n' && ch != '\0' && !feof ( fp ))
|
||||||
|
{
|
||||||
|
if ( ch == '\r' )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ( !( line = (char*) realloc ( line, ++nbytes )))
|
||||||
|
{
|
||||||
|
AI_fatal_err ( "Fatal dynamic memory allocation", __FILE__, __LINE__ );
|
||||||
|
}
|
||||||
|
|
||||||
|
line[ nbytes - 2 ] = ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( nbytes > 1 )
|
||||||
|
{
|
||||||
|
line[ nbytes - 1 ] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return line;
|
||||||
|
} /* ----- end of function __AI_getline ----- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Manage a client connection
|
||||||
|
* \param arg void* reference to the client socket
|
||||||
|
*/
|
||||||
|
|
||||||
|
PRIVATE void*
|
||||||
|
__AI_webservlet_thread ( void *arg )
|
||||||
|
{
|
||||||
|
time_t ltime = time ( NULL );
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
FILE *sock = NULL,
|
||||||
|
*fp = NULL;
|
||||||
|
|
||||||
|
int i,
|
||||||
|
*sd = (int*) arg,
|
||||||
|
nlines = 0,
|
||||||
|
nmatches = 0,
|
||||||
|
max_content_length = 0,
|
||||||
|
max_headers_length = 0,
|
||||||
|
req_file_absolute_path_size = 0;
|
||||||
|
|
||||||
|
char *line = NULL,
|
||||||
|
*unescaped = NULL,
|
||||||
|
*http_response = NULL,
|
||||||
|
*http_headers = NULL,
|
||||||
|
*strtime = NULL,
|
||||||
|
**matches = NULL,
|
||||||
|
*req_file_absolute_path = NULL,
|
||||||
|
content_type[108] = { 0 },
|
||||||
|
extension[100] = { 0 },
|
||||||
|
http_ver[10] = { 0 },
|
||||||
|
req_file[1024] = { 0 };
|
||||||
|
|
||||||
|
max_content_length = strlen ( HTTP_ERR_RESPONSE_FORMAT ) + strlen ( config->webserv_banner ) + 1000;
|
||||||
|
max_headers_length = strlen ( HTTP_RESPONSE_HEADERS_FORMAT ) + strlen ( config->webserv_banner ) + 1000;
|
||||||
|
|
||||||
|
if ( !( http_response = (char*) alloca ( max_content_length )))
|
||||||
|
{
|
||||||
|
pthread_exit ((void*) 0);
|
||||||
|
return (void*) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !( http_headers = (char*) alloca ( max_headers_length )))
|
||||||
|
{
|
||||||
|
pthread_exit ((void*) 0);
|
||||||
|
return (void*) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !( sock = fdopen ( *sd, "r+" )))
|
||||||
|
{
|
||||||
|
pthread_exit ((void*) 0);
|
||||||
|
return (void*) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
req_file_absolute_path_size = sizeof ( req_file ) + strlen ( config->webserv_dir ) + 1;
|
||||||
|
|
||||||
|
if ( !( req_file_absolute_path = (char*) alloca ( req_file_absolute_path_size )))
|
||||||
|
{
|
||||||
|
pthread_exit ((void*) 0);
|
||||||
|
return (void*) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( nlines=0; ( line = __AI_getline ( sock )); nlines++ )
|
||||||
|
{
|
||||||
|
if ( preg_match ( "^\\s*GET\\s+(/[^ ]*)\\s*(HTTP/[0-9]\\.[0-9])?", line, &matches, &nmatches ) > 0 )
|
||||||
|
{
|
||||||
|
if ( strlen ( matches[1] ) > 0 )
|
||||||
|
{
|
||||||
|
strncpy ( http_ver, matches[1], sizeof ( http_ver ));
|
||||||
|
} else {
|
||||||
|
strncpy ( http_ver, "HTTP/1.0", sizeof ( http_ver ));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !strcmp ( matches[0], "/" ))
|
||||||
|
{
|
||||||
|
free ( matches[0] );
|
||||||
|
matches[0] = strdup ( "/index.html" );
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf ( req_file_absolute_path, req_file_absolute_path_size, "%s%s", config->webserv_dir, matches[0] );
|
||||||
|
|
||||||
|
if ( strcmp ( http_ver, "HTTP/1.0" ) && strcmp ( http_ver, "HTTP/1.1" ))
|
||||||
|
{
|
||||||
|
snprintf ( http_response, max_content_length, HTTP_ERR_RESPONSE_FORMAT,
|
||||||
|
400, "Bad Request", "Bad Request",
|
||||||
|
"The request could not be understood by the server due to malformed syntax",
|
||||||
|
config->webserv_banner );
|
||||||
|
|
||||||
|
ltime = time ( NULL );
|
||||||
|
strtime = strdup ( ctime ( <ime ));
|
||||||
|
strtime [ strlen(strtime) - 1 ] = 0;
|
||||||
|
snprintf ( http_headers, max_headers_length, HTTP_RESPONSE_HEADERS_FORMAT,
|
||||||
|
"HTTP/1.1", 400, "Bad Request", strtime,
|
||||||
|
config->webserv_banner, "text/html", strlen ( http_response ));
|
||||||
|
free ( strtime );
|
||||||
|
free ( line );
|
||||||
|
line = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( i=0; i < nmatches; i++ )
|
||||||
|
{
|
||||||
|
free ( matches[i] );
|
||||||
|
}
|
||||||
|
|
||||||
|
free ( matches );
|
||||||
|
matches = NULL;
|
||||||
|
|
||||||
|
if ( !( unescaped = (char*) alloca ( strlen ( req_file_absolute_path ) + 2 )))
|
||||||
|
{
|
||||||
|
pthread_exit ((void*) 0);
|
||||||
|
return (void*) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Avoid directory traversal */
|
||||||
|
__AI_url_unescape ( req_file_absolute_path, &unescaped, strlen ( req_file_absolute_path ));
|
||||||
|
unescaped = str_replace_all ( unescaped, "../", "" );
|
||||||
|
strncpy ( req_file_absolute_path, unescaped, req_file_absolute_path_size );
|
||||||
|
free ( unescaped );
|
||||||
|
|
||||||
|
if ( stat ( req_file_absolute_path, &st ) < 0 )
|
||||||
|
{
|
||||||
|
snprintf ( http_response, max_content_length, HTTP_ERR_RESPONSE_FORMAT,
|
||||||
|
404, "Not Found", "Not Found",
|
||||||
|
"The requested resource was not found on the server",
|
||||||
|
config->webserv_banner );
|
||||||
|
|
||||||
|
ltime = time ( NULL );
|
||||||
|
strtime = strdup ( ctime ( <ime ));
|
||||||
|
strtime [ strlen(strtime) - 1 ] = 0;
|
||||||
|
snprintf ( http_headers, max_headers_length, HTTP_RESPONSE_HEADERS_FORMAT,
|
||||||
|
http_ver, 404, "Not Found", strtime,
|
||||||
|
config->webserv_banner, "text/html", strlen ( http_response ));
|
||||||
|
free ( strtime );
|
||||||
|
free ( line );
|
||||||
|
line = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !( fp = fopen ( req_file_absolute_path, "r" ))) {
|
||||||
|
snprintf ( http_response, max_content_length, HTTP_ERR_RESPONSE_FORMAT,
|
||||||
|
403, "Forbidden", "Forbidden",
|
||||||
|
"The client does not have enough permissions for accessing the requested resource on the server",
|
||||||
|
config->webserv_banner );
|
||||||
|
|
||||||
|
ltime = time ( NULL );
|
||||||
|
strtime = strdup ( ctime ( <ime ));
|
||||||
|
strtime [ strlen(strtime) - 1 ] = 0;
|
||||||
|
snprintf ( http_headers, max_headers_length, HTTP_RESPONSE_HEADERS_FORMAT,
|
||||||
|
http_ver, 403, "Forbidden", strtime,
|
||||||
|
config->webserv_banner, "text/html", strlen ( http_response ));
|
||||||
|
free ( strtime );
|
||||||
|
free ( line );
|
||||||
|
line = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( preg_match ( "\\.([a-zA-Z0-9]+)$", req_file_absolute_path, &matches, &nmatches ) > 0 )
|
||||||
|
{
|
||||||
|
if ( strlen ( matches[0] ) < sizeof ( extension ))
|
||||||
|
{
|
||||||
|
strncpy ( extension, matches[0], sizeof ( extension ));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !strcasecmp ( extension, "html" ))
|
||||||
|
{
|
||||||
|
strncpy ( content_type, "text/html", sizeof ( content_type ));
|
||||||
|
} else if ( !strcasecmp ( extension, "css" ))
|
||||||
|
{
|
||||||
|
strncpy ( content_type, "text/css", sizeof ( content_type ));
|
||||||
|
} else if ( !strcasecmp ( extension, "js" ))
|
||||||
|
{
|
||||||
|
strncpy ( content_type, "application/x-javascript", sizeof ( content_type ));
|
||||||
|
} else if ( !strcasecmp ( extension, "jpg" ) || !strcasecmp ( extension, "jpeg" )) {
|
||||||
|
strncpy ( content_type, "image/jpeg", sizeof ( content_type ));
|
||||||
|
} else if ( !strcasecmp ( extension, "gif" ) || !strcasecmp ( extension, "png" ) ||
|
||||||
|
!strcasecmp ( extension, "bmp" ) || !strcasecmp ( extension, "tif" ) ||
|
||||||
|
!strcasecmp ( extension, "ppm" )) {
|
||||||
|
snprintf ( content_type, sizeof ( content_type ),
|
||||||
|
"image/%s", extension );
|
||||||
|
} else {
|
||||||
|
strncpy ( content_type, "text/plain", sizeof ( content_type ));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !( http_response = (char*) alloca ( st.st_size + 2 )))
|
||||||
|
{
|
||||||
|
pthread_exit ((void*) 0);
|
||||||
|
return (void*) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset ( http_response, 0, st.st_size + 2 );
|
||||||
|
fread ( http_response, st.st_size, 1, fp );
|
||||||
|
fclose ( fp );
|
||||||
|
|
||||||
|
ltime = time ( NULL );
|
||||||
|
strtime = strdup ( ctime ( <ime ));
|
||||||
|
strtime [ strlen(strtime) - 1 ] = 0;
|
||||||
|
snprintf ( http_headers, max_headers_length, HTTP_RESPONSE_HEADERS_FORMAT,
|
||||||
|
http_ver, 200, "Found", strtime, config->webserv_banner,
|
||||||
|
content_type, strlen ( http_response ));
|
||||||
|
free ( strtime );
|
||||||
|
free ( line );
|
||||||
|
line = NULL;
|
||||||
|
continue;
|
||||||
|
} else if ( nlines == 0 ) {
|
||||||
|
snprintf ( http_response, max_content_length, HTTP_ERR_RESPONSE_FORMAT,
|
||||||
|
405, "Method Not Allowed", "Method Not Allowed",
|
||||||
|
"The requested HTTP method is not allowed",
|
||||||
|
config->webserv_banner );
|
||||||
|
|
||||||
|
ltime = time ( NULL );
|
||||||
|
strtime = strdup ( ctime ( <ime ));
|
||||||
|
strtime [ strlen(strtime) - 1 ] = 0;
|
||||||
|
snprintf ( http_headers, max_headers_length, HTTP_RESPONSE_HEADERS_FORMAT,
|
||||||
|
"HTTP/1.1", 405, "Method Not Allowed", strtime, "text/html",
|
||||||
|
config->webserv_banner, strlen ( http_response ));
|
||||||
|
free ( strtime );
|
||||||
|
free ( line );
|
||||||
|
line = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
free ( line );
|
||||||
|
line = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf ( sock, "%s%s", http_headers, http_response );
|
||||||
|
fclose ( sock );
|
||||||
|
close ( *sd );
|
||||||
|
pthread_exit ( 0 );
|
||||||
|
} /* ----- end of function __AI_webservlet_thread ----- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Thread running the code of the web server for the web interface of the module
|
||||||
|
*/
|
||||||
|
|
||||||
|
void*
|
||||||
|
AI_webserv_thread ( void *arg )
|
||||||
|
{
|
||||||
|
int on = 1,
|
||||||
|
sd,
|
||||||
|
client_sd;
|
||||||
|
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
pthread_t servlet_thread;
|
||||||
|
pthread_attr_t attr;
|
||||||
|
|
||||||
|
if (( sd = socket ( AF_INET, SOCK_STREAM, 0 )) < 0 )
|
||||||
|
{
|
||||||
|
AI_fatal_err ( "Error while creating webserver socket", __FILE__, __LINE__ );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( setsockopt ( sd, SOL_SOCKET, SO_REUSEADDR, (void*) &on, sizeof ( on )) < 0 )
|
||||||
|
{
|
||||||
|
AI_fatal_err ( "Error while setting SO_REUSEADDR on the socket", __FILE__, __LINE__ );
|
||||||
|
}
|
||||||
|
|
||||||
|
memset ( &addr, 0, sizeof ( struct sockaddr_in ));
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons ( config->webserv_port );
|
||||||
|
addr.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
|
||||||
|
if ( bind ( sd, (struct sockaddr*) &addr, sizeof ( addr )) < 0 )
|
||||||
|
{
|
||||||
|
AI_fatal_err ( "Error while binding socket", __FILE__, __LINE__ );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( listen ( sd, 10 ) < 0 )
|
||||||
|
{
|
||||||
|
AI_fatal_err ( "Error while setting the socket in listen mode", __FILE__, __LINE__ );
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_attr_init ( &attr );
|
||||||
|
pthread_attr_setdetachstate ( &attr, PTHREAD_CREATE_DETACHED );
|
||||||
|
|
||||||
|
while ( 1 )
|
||||||
|
{
|
||||||
|
if (( client_sd = accept ( sd, NULL, NULL )) < 0 )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ( pthread_create ( &servlet_thread, &attr, __AI_webservlet_thread, (void*) &client_sd ) != 0 )
|
||||||
|
{
|
||||||
|
AI_fatal_err ( "Error while creating the webservlet thread", __FILE__, __LINE__ );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close ( sd );
|
||||||
|
pthread_exit ((void*) 0);
|
||||||
|
return (void*) 0;
|
||||||
|
} /* ----- end of function AI_webserv_thread ----- */
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
Loading…
Reference in a new issue