4
mirror of git://git.acid.vegas/unrealircd.git synced 2024-12-28 09:16:38 +00:00

Updated to 5.2.0.1

This commit is contained in:
Dionysus 2021-06-19 11:52:51 -04:00
parent 72c7ba196c
commit fc973f2667
Signed by: acidvegas
GPG Key ID: EF4B922DB85DC9DE
95 changed files with 7213 additions and 1880 deletions

31
Config
View File

@ -95,8 +95,19 @@ fi
if [ "x$MAKE" = "x" ]; then if [ "x$MAKE" = "x" ]; then
if [ "x$MAKEFLAGS" = "x" ]; then if [ "x$MAKEFLAGS" = "x" ]; then
if make --version 2>&1|grep -q "GNU Make"; then if make --version 2>&1|grep -q "GNU Make"; then
echo "Running with 4 concurrent build processes by default (make -j4)." LOWMEM=0
export MAKE='make -j4' if [ -f /proc/meminfo ]; then
FREEKB="`cat /proc/meminfo |grep MemAvailable|awk '{ print $2 }'`"
if [ "$FREEKB" != "" -a "$FREEKB" -lt 768000 ]; then
LOWMEM=1
fi
fi
if [ "$LOWMEM" = 0 ]; then
echo "Running with 4 concurrent build processes by default (make -j4)."
export MAKE='make -j4'
else
echo "System detected with less than 750MB available memory, not forcing parallel build."
fi
fi fi
fi fi
fi fi
@ -336,7 +347,7 @@ echo "We will now ask you a number of questions. You can just press ENTER to acc
echo "" echo ""
# This needs to be updated each release so auto-upgrading works for settings, modules, etc!!: # This needs to be updated each release so auto-upgrading works for settings, modules, etc!!:
UNREALRELEASES="unrealircd-5.0.9-rc1 unrealircd-5.0.8 unrealircd-5.0.8-rc1 unrealircd-5.0.7 unrealircd-5.0.7-rc1 unrealircd-5.0.6 unrealircd-5.0.5.1 unrealircd-5.0.5 unrealircd-5.0.4 unrealircd-5.0.3.1 unrealircd-5.0.3 unrealircd-5.0.2 unrealircd-5.0.1 unrealircd-5.0.0" UNREALRELEASES="unrealircd-5.2.0 unrealircd-5.2.0-rc1 unrealircd-5.0.9.1 unrealircd-5.0.9 unrealircd-5.0.9-rc1 unrealircd-5.0.8 unrealircd-5.0.8-rc1 unrealircd-5.0.7 unrealircd-5.0.7-rc1 unrealircd-5.0.6 unrealircd-5.0.5.1 unrealircd-5.0.5 unrealircd-5.0.4 unrealircd-5.0.3.1 unrealircd-5.0.3 unrealircd-5.0.2 unrealircd-5.0.1 unrealircd-5.0.0"
if [ -f "config.settings" ]; then if [ -f "config.settings" ]; then
. ./config.settings . ./config.settings
else else
@ -639,6 +650,13 @@ if [ "$REMOTEINC" = "1" ] ; then
CURLDIR=`eval echo $cc` # modified CURLDIR=`eval echo $cc` # modified
fi fi
done done
if [ "x$CURLDIR" != "x" ]; then
"$CURLDIR/bin/curl-config" --libs 1>/dev/null 2>&1
if [ "$?" != 0 ]; then
echo "Curl from $CURLDIR seems unusable ($CURLDIR/bin/curl-config does not exist)"
CURLDIR=""
fi
fi
fi fi
fi fi
@ -749,6 +767,13 @@ fi
rm -f config.settings rm -f config.settings
cat > config.settings << __EOF__ cat > config.settings << __EOF__
# #
# These are the settings saved from running './Config'.
# Note that it is not recommended to edit config.settings by hand!
# Chances are you misunderstand what a variable does or what the
# supported values are. You better just re-run the ./Config script
# and answer appropriately there, to get a correct config.settings
# file.
#
BASEPATH="$BASEPATH" BASEPATH="$BASEPATH"
BINDIR="$BINDIR" BINDIR="$BINDIR"
DATADIR="$DATADIR" DATADIR="$DATADIR"

View File

@ -34,11 +34,11 @@ FROMDOS=/home/cmunk/bin/4dos
# #
#XCFLAGS=-O -g -export-dynamic #XCFLAGS=-O -g -export-dynamic
IRCDLIBS=@IRCDLIBS@ @PCRE2_LIBS@ @ARGON2_LIBS@ @CARES_LIBS@ @PTHREAD_LIBS@ IRCDLIBS=@IRCDLIBS@ @PCRE2_LIBS@ @ARGON2_LIBS@ @CARES_LIBS@ @SODIUM_LIBS@ @PTHREAD_LIBS@
CRYPTOLIB=@CRYPTOLIB@ CRYPTOLIB=@CRYPTOLIB@
OPENSSLINCLUDES= OPENSSLINCLUDES=
XCFLAGS=@PTHREAD_CFLAGS@ @PCRE2_CFLAGS@ @ARGON2_CFLAGS@ @CARES_CFLAGS@ @CFLAGS@ @HARDEN_CFLAGS@ @CPPFLAGS@ XCFLAGS=@PTHREAD_CFLAGS@ @PCRE2_CFLAGS@ @ARGON2_CFLAGS@ @CARES_CFLAGS@ @SODIUM_CFLAGS@ @CFLAGS@ @HARDEN_CFLAGS@ @CPPFLAGS@
# #
# use the following on MIPS: # use the following on MIPS:
#CFLAGS= -systype bsd43 -DSYSTYPE_BSD43 -I$(INCLUDEDIR) #CFLAGS= -systype bsd43 -DSYSTYPE_BSD43 -I$(INCLUDEDIR)

View File

@ -24,6 +24,11 @@ MT=mt
#ARGON2_INC_DIR="C:\dev\argon2\include" #ARGON2_INC_DIR="C:\dev\argon2\include"
#ARGON2LIB="Argon2RefDll.lib" #ARGON2LIB="Argon2RefDll.lib"
### SODIUM ###
#SODIUM_LIB_DIR="C:\dev\unrealircd-5-libs\libsodium\......."
#SODIUM_INC_DIR="C:\dev\unrealircd-5-libs\libsodium\......."
#SODIUMLIB="libsodium.lib"
### C-ARES #### ### C-ARES ####
#CARES_LIB_DIR="C:\dev\c-ares\vc\cares\dll-release" #CARES_LIB_DIR="C:\dev\c-ares\vc\cares\dll-release"
#CARES_INC_DIR="C:\dev\c-ares" #CARES_INC_DIR="C:\dev\c-ares"
@ -94,6 +99,13 @@ ARGON2_INC=/I "$(ARGON2_INC_DIR)"
ARGON2_LIB=/LIBPATH:"$(ARGON2_LIB_DIR)" ARGON2_LIB=/LIBPATH:"$(ARGON2_LIB_DIR)"
!ENDIF !ENDIF
!IFDEF SODIUM_INC_DIR
SODIUM_INC=/I "$(SODIUM_INC_DIR)"
!ENDIF
!IFDEF SODIUM_LIB_DIR
SODIUM_LIB=/LIBPATH:"$(SODIUM_LIB_DIR)"
!ENDIF
!IFDEF USE_REMOTEINC !IFDEF USE_REMOTEINC
CURLCFLAGS=/D USE_LIBCURL CURLCFLAGS=/D USE_LIBCURL
CURLOBJ=SRC/URL.OBJ CURLOBJ=SRC/URL.OBJ
@ -125,13 +137,13 @@ DBGLFLAG=/debug
MODDBGCFLAG=/LDd /MD /Zi MODDBGCFLAG=/LDd /MD /Zi
!ENDIF !ENDIF
STDOPTIONS=$(PCRE2_INC) $(ARGON2_INC) $(CARES_INC) $(LIBCURL_INC) $(LIBRESSL_INC) /J /I ./INCLUDE /nologo \ STDOPTIONS=$(PCRE2_INC) $(ARGON2_INC) $(SODIUM_INC) $(CARES_INC) $(LIBCURL_INC) $(LIBRESSL_INC) /J /I ./INCLUDE /nologo \
$(CURLCFLAGS) /D FD_SETSIZE=16384 $(SSLCFLAGS) /D _CRT_SECURE_NO_DEPRECATE /D _CRT_NONSTDC_NO_DEPRECATE \ $(CURLCFLAGS) /D FD_SETSIZE=16384 $(SSLCFLAGS) /D _CRT_SECURE_NO_DEPRECATE /D _CRT_NONSTDC_NO_DEPRECATE \
/D FAKELAG_CONFIGURABLE=1 \ /D FAKELAG_CONFIGURABLE=1 \
/W3 /wd4267 /wd4101 /wd4018 /wd4244 /wd4996 /WX \ /W3 /wd4267 /wd4101 /wd4018 /wd4244 /wd4996 /WX \
/analyze:ruleset extras\VStudioAnalyze.ruleset /analyze:ruleset extras\VStudioAnalyze.ruleset
STDLIBS=$(CARES_LIB) $(CARESLIB) $(PCRE2_LIB) $(PCRE2LIB) $(ARGON2_LIB) $(ARGON2LIB) \ STDLIBS=$(CARES_LIB) $(CARESLIB) $(PCRE2_LIB) $(PCRE2LIB) $(ARGON2_LIB) $(ARGON2LIB) \
$(LIBRESSL_LIB) $(SSLLIB) $(LIBCURL_LIB) $(CURLLIB) $(SODIUM_LIB) $(SODIUMLIB) $(LIBRESSL_LIB) $(SSLLIB) $(LIBCURL_LIB) $(CURLLIB)
CFLAGS=$(DBGCFLAG) $(STDOPTIONS) /c /Fosrc/ CFLAGS=$(DBGCFLAG) $(STDOPTIONS) /c /Fosrc/
CFLAGSST=$(DBGCFLAGST) $(STDOPTIONS) /c /Fosrc/ CFLAGSST=$(DBGCFLAGST) $(STDOPTIONS) /c /Fosrc/
LFLAGS=kernel32.lib user32.lib gdi32.lib shell32.lib ws2_32.lib advapi32.lib \ LFLAGS=kernel32.lib user32.lib gdi32.lib shell32.lib ws2_32.lib advapi32.lib \
@ -161,7 +173,7 @@ EXP_OBJ_FILES=SRC/CHANNEL.OBJ SRC/SEND.OBJ SRC/SOCKET.OBJ \
SRC/DISPATCH.OBJ SRC/API-ISUPPORT.OBJ SRC/API-COMMAND.OBJ \ SRC/DISPATCH.OBJ SRC/API-ISUPPORT.OBJ SRC/API-COMMAND.OBJ \
SRC/API-CLICAP.OBJ SRC/API-MESSAGETAG.OBJ SRC/API-HISTORY-BACKEND.OBJ \ SRC/API-CLICAP.OBJ SRC/API-MESSAGETAG.OBJ SRC/API-HISTORY-BACKEND.OBJ \
SRC/API-EXTBAN.OBJ SRC/API-EFUNCTIONS.OBJ SRC/CRYPT_BLOWFISH.OBJ \ SRC/API-EXTBAN.OBJ SRC/API-EFUNCTIONS.OBJ SRC/CRYPT_BLOWFISH.OBJ \
SRC/OPERCLASS.OBJ SRC/UPDCONF.OBJ SRC/CRASHREPORT.OBJ \ SRC/OPERCLASS.OBJ SRC/UPDCONF.OBJ SRC/CRASHREPORT.OBJ SRC/UNREALDB.OBJ \
SRC/OPENSSL_HOSTNAME_VALIDATION.OBJ \ SRC/OPENSSL_HOSTNAME_VALIDATION.OBJ \
SRC/UTF8.OBJ $(CURLOBJ) SRC/UTF8.OBJ $(CURLOBJ)
@ -289,9 +301,12 @@ DLL_FILES=SRC/MODULES/CLOAK.DLL \
SRC/MODULES/ECHO-MESSAGE.DLL \ SRC/MODULES/ECHO-MESSAGE.DLL \
SRC/MODULES/USERIP-TAG.DLL \ SRC/MODULES/USERIP-TAG.DLL \
SRC/MODULES/USERHOST-TAG.DLL \ SRC/MODULES/USERHOST-TAG.DLL \
SRC/MODULES/BOT-TAG.DLL \
SRC/MODULES/REPLY-TAG.DLL \
SRC/MODULES/REQUIRE-MODULE.DLL \ SRC/MODULES/REQUIRE-MODULE.DLL \
SRC/MODULES/IDENT_LOOKUP.DLL \ SRC/MODULES/IDENT_LOOKUP.DLL \
SRC/MODULES/HISTORY.DLL \ SRC/MODULES/HISTORY.DLL \
SRC/MODULES/CHATHISTORY.DLL \
SRC/MODULES/TARGETFLOODPROT.DLL \ SRC/MODULES/TARGETFLOODPROT.DLL \
SRC/MODULES/TYPING-INDICATOR.DLL \ SRC/MODULES/TYPING-INDICATOR.DLL \
SRC/MODULES/CLIENTTAGDENY.DLL SRC/MODULES/CLIENTTAGDENY.DLL
@ -530,6 +545,12 @@ src/operclass.obj: src/operclass.c $(INCLUDES) ./include/dbuf.h
src/updconf.obj: src/updconf.c $(INCLUDES) ./include/dbuf.h src/updconf.obj: src/updconf.c $(INCLUDES) ./include/dbuf.h
$(CC) $(CFLAGS) src/updconf.c $(CC) $(CFLAGS) src/updconf.c
src/crashreport.obj: src/crashreport.c $(INCLUDES) ./include/dbuf.h
$(CC) $(CFLAGS) src/crashreport.c
src/unrealdb.obj: src/unrealdb.c $(INCLUDES) ./include/dbuf.h
$(CC) $(CFLAGS) src/unrealdb.c
src/utf8.obj: src/utf8.c $(INCLUDES) ./include/dbuf.h src/utf8.obj: src/utf8.c $(INCLUDES) ./include/dbuf.h
$(CC) $(CFLAGS) src/utf8.c $(CC) $(CFLAGS) src/utf8.c
@ -1079,6 +1100,12 @@ src/modules/userip-tag.dll: src/modules/userip-tag.c $(INCLUDES)
src/modules/userhost-tag.dll: src/modules/userhost-tag.c $(INCLUDES) src/modules/userhost-tag.dll: src/modules/userhost-tag.c $(INCLUDES)
$(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/userhost-tag.c $(MODLFLAGS) $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/userhost-tag.c $(MODLFLAGS)
src/modules/bot-tag.dll: src/modules/bot-tag.c $(INCLUDES)
$(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/bot-tag.c $(MODLFLAGS)
src/modules/reply-tag.dll: src/modules/reply-tag.c $(INCLUDES)
$(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/reply-tag.c $(MODLFLAGS)
src/modules/require-module.dll: src/modules/require-module.c $(INCLUDES) src/modules/require-module.dll: src/modules/require-module.c $(INCLUDES)
$(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/require-module.c $(MODLFLAGS) $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/require-module.c $(MODLFLAGS)
@ -1088,6 +1115,9 @@ src/modules/ident_lookup.dll: src/modules/ident_lookup.c $(INCLUDES)
src/modules/history.dll: src/modules/history.c $(INCLUDES) src/modules/history.dll: src/modules/history.c $(INCLUDES)
$(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/history.c $(MODLFLAGS) $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/history.c $(MODLFLAGS)
src/modules/chathistory.dll: src/modules/chathistory.c $(INCLUDES)
$(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/chathistory.c $(MODLFLAGS)
src/modules/targetfloodprot.dll: src/modules/targetfloodprot.c $(INCLUDES) src/modules/targetfloodprot.dll: src/modules/targetfloodprot.c $(INCLUDES)
$(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/targetfloodprot.c $(MODLFLAGS) $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/targetfloodprot.c $(MODLFLAGS)

1262
configure vendored

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ dnl src/windows/unrealinst.iss
dnl doc/Config.header dnl doc/Config.header
dnl src/version.c.SH dnl src/version.c.SH
AC_INIT([unrealircd], [5.0.9], [https://bugs.unrealircd.org/], [], [https://unrealircd.org/]) AC_INIT([unrealircd], [5.2.0.1], [https://bugs.unrealircd.org/], [], [https://unrealircd.org/])
AC_CONFIG_SRCDIR([src/ircd.c]) AC_CONFIG_SRCDIR([src/ircd.c])
AC_CONFIG_HEADER([include/setup.h]) AC_CONFIG_HEADER([include/setup.h])
AC_CONFIG_AUX_DIR([autoconf]) AC_CONFIG_AUX_DIR([autoconf])
@ -30,17 +30,17 @@ UNREAL_VERSION_GENERATION=["5"]
AC_DEFINE_UNQUOTED([UNREAL_VERSION_GENERATION], [$UNREAL_VERSION_GENERATION], [Generation version number (e.g.: X for X.Y.Z)]) AC_DEFINE_UNQUOTED([UNREAL_VERSION_GENERATION], [$UNREAL_VERSION_GENERATION], [Generation version number (e.g.: X for X.Y.Z)])
# Major version number (e.g.: Y in X.Y.Z) # Major version number (e.g.: Y in X.Y.Z)
UNREAL_VERSION_MAJOR=["0"] UNREAL_VERSION_MAJOR=["2"]
AC_DEFINE_UNQUOTED([UNREAL_VERSION_MAJOR], [$UNREAL_VERSION_MAJOR], [Major version number (e.g.: Y for X.Y.Z)]) AC_DEFINE_UNQUOTED([UNREAL_VERSION_MAJOR], [$UNREAL_VERSION_MAJOR], [Major version number (e.g.: Y for X.Y.Z)])
# Minor version number (e.g.: Z in X.Y.Z) # Minor version number (e.g.: Z in X.Y.Z)
UNREAL_VERSION_MINOR=["9"] UNREAL_VERSION_MINOR=["0"]
AC_DEFINE_UNQUOTED([UNREAL_VERSION_MINOR], [$UNREAL_VERSION_MINOR], [Minor version number (e.g.: Z for X.Y.Z)]) AC_DEFINE_UNQUOTED([UNREAL_VERSION_MINOR], [$UNREAL_VERSION_MINOR], [Minor version number (e.g.: Z for X.Y.Z)])
# The version suffix such as a beta marker or release candidate # The version suffix such as a beta marker or release candidate
# marker. (e.g.: -rcX for unrealircd-3.2.9-rcX). This macro is a # marker. (e.g.: -rcX for unrealircd-3.2.9-rcX). This macro is a
# string instead of an integer because it contains arbitrary data. # string instead of an integer because it contains arbitrary data.
UNREAL_VERSION_SUFFIX=[""] UNREAL_VERSION_SUFFIX=[".1"]
AC_DEFINE_UNQUOTED([UNREAL_VERSION_SUFFIX], ["$UNREAL_VERSION_SUFFIX"], [Version suffix such as a beta marker or release candidate marker. (e.g.: -rcX for unrealircd-3.2.9-rcX)]) AC_DEFINE_UNQUOTED([UNREAL_VERSION_SUFFIX], ["$UNREAL_VERSION_SUFFIX"], [Version suffix such as a beta marker or release candidate marker. (e.g.: -rcX for unrealircd-3.2.9-rcX)])
AC_PATH_PROG(RM,rm) AC_PATH_PROG(RM,rm)
@ -83,6 +83,18 @@ AC_CHECK_LIB(descrypt, crypt,
[AC_DEFINE([HAVE_CRYPT], [], [Define if you have crypt]) [AC_DEFINE([HAVE_CRYPT], [], [Define if you have crypt])
IRCDLIBS="$IRCDLIBS-lcrypt "])]) IRCDLIBS="$IRCDLIBS-lcrypt "])])
dnl Check for big-endian system, even though these hardly exist anymore...
AS_CASE([$host_cpu],
[i?86|amd64|x86_64],
[ac_cv_c_bigendian=no]
)
AC_C_BIGENDIAN(
AC_DEFINE(NATIVE_BIG_ENDIAN, 1, [machine is bigendian]),
AC_DEFINE(NATIVE_LITTLE_ENDIAN, 1, [machine is littleendian]),
AC_MSG_ERROR([unknown endianness]),
AC_MSG_ERROR([universal endianness is not supported - compile separately and use lipo(1)])
)
dnl HARDENING START dnl HARDENING START
dnl This is taken from https://github.com/kmcallister/autoharden dnl This is taken from https://github.com/kmcallister/autoharden
dnl With some very small modifications (to remove C++ checking for instance) dnl With some very small modifications (to remove C++ checking for instance)
@ -499,6 +511,7 @@ AC_ARG_WITH(operoverride-verify, [AS_HELP_STRING([--with-operoverride-verify], [
[AC_DEFINE([OPEROVERRIDE_VERIFY], [], [Define if you want opers to have to use /invite to join +s/+p channels])])]) [AC_DEFINE([OPEROVERRIDE_VERIFY], [], [Define if you want opers to have to use /invite to join +s/+p channels])])])
AC_ARG_WITH(system-pcre2, [AS_HELP_STRING([--without-system-pcre2], [Use the system pcre2 package instead of bundled, discovered using pkg-config])], [], [with_system_pcre2=yes]) AC_ARG_WITH(system-pcre2, [AS_HELP_STRING([--without-system-pcre2], [Use the system pcre2 package instead of bundled, discovered using pkg-config])], [], [with_system_pcre2=yes])
AC_ARG_WITH(system-argon2, [AS_HELP_STRING([--without-system-argon2], [Use bundled version instead of system argon2 library. Normally autodetected via pkg-config])], [], [with_system_argon2=yes]) AC_ARG_WITH(system-argon2, [AS_HELP_STRING([--without-system-argon2], [Use bundled version instead of system argon2 library. Normally autodetected via pkg-config])], [], [with_system_argon2=yes])
AC_ARG_WITH(system-sodium, [AS_HELP_STRING([--without-system-sodium], [Use bundled version instead of system sodium library. Normally autodetected via pkg-config])], [], [with_system_sodium=yes])
AC_ARG_WITH(system-cares, [AS_HELP_STRING([--without-system-cares], [Use bundled version instead of system c-ares. Normally autodetected via pkg-config.])], [], [with_system_cares=yes]) AC_ARG_WITH(system-cares, [AS_HELP_STRING([--without-system-cares], [Use bundled version instead of system c-ares. Normally autodetected via pkg-config.])], [], [with_system_cares=yes])
CHECK_SSL CHECK_SSL
CHECK_SSL_CTX_SET1_CURVES_LIST CHECK_SSL_CTX_SET1_CURVES_LIST
@ -625,6 +638,55 @@ AC_SUBST(ARGON2_LIBS)
cd $cur_dir cd $cur_dir
]) ])
dnl Use system sodium when available, unless --without-system-sodium
has_system_sodium="no"
AS_IF([test "x$with_system_sodium" = "xyes"],[
PKG_CHECK_MODULES([SODIUM], [libsodium >= 1.0.16],[has_system_sodium=yes
AS_IF([test "x$PRIVATELIBDIR" != "x"], [rm -f "$PRIVATELIBDIR/"libsodium*])],[has_system_sodium=no])])
AS_IF([test "$has_system_sodium" = "no"],[
dnl REMEMBER TO CHANGE WITH A NEW SODIUM RELEASE!
sodium_version="1.0.18"
AC_MSG_RESULT(extracting sodium library)
cur_dir=`pwd`
cd extras
dnl remove old sodium directory to force a recompile...
dnl and remove its installation prefix just to clean things up.
rm -rf sodium-$sodium_version sodium
if test "x$ac_cv_path_GUNZIP" = "x" ; then
tar xfz libsodium.tar.gz
else
cp libsodium.tar.gz libsodium.tar.gz.bak
gunzip -f libsodium.tar.gz
cp libsodium.tar.gz.bak libsodium.tar.gz
tar xf libsodium.tar
fi
AC_MSG_RESULT(compiling sodium library)
cd libsodium-$sodium_version
save_cflags="$CFLAGS"
CFLAGS="$orig_cflags"
export CFLAGS
./configure --prefix=$cur_dir/extras/sodium --libdir=$PRIVATELIBDIR --enable-shared --disable-static --enable-opt || exit 1
CFLAGS="$save_cflags"
AC_MSG_RESULT(compiling sodium resolver library)
$ac_cv_prog_MAKER || exit 1
AC_MSG_RESULT(installing sodium resolver library)
$ac_cv_prog_MAKER install || exit 1
SODIUM_CFLAGS="-I$cur_dir/extras/sodium/include"
AC_SUBST(SODIUM_CFLAGS)
SODIUM_LIBS=
dnl See c-ares's compilation section for more info on this hack.
dnl ensure that we're linking against the bundled version
dnl (we only reach this code if linking against the bundled version is desired).
AS_IF([test -n "$ac_cv_path_PKGCONFIG"],
[SODIUM_LIBS="`$ac_cv_path_PKGCONFIG --libs libsodium.pc`"])
dnl For when pkg-config isn't available
AS_IF([test -z "$SODIUM_LIBS"],
[SODIUM_LIBS="-L$PRIVATELIBDIR -lsodium"])
AC_SUBST(SODIUM_LIBS)
cd $cur_dir
])
dnl Use system c-ares when available, unless --without-system-cares. dnl Use system c-ares when available, unless --without-system-cares.
has_system_cares="no" has_system_cares="no"
AS_IF([test "x$with_system_cares" = "xyes"],[ AS_IF([test "x$with_system_cares" = "xyes"],[
@ -654,7 +716,7 @@ cd c-ares-$cares_version
save_cflags="$CFLAGS" save_cflags="$CFLAGS"
CFLAGS="$orig_cflags" CFLAGS="$orig_cflags"
export CFLAGS export CFLAGS
./configure --prefix=$cur_dir/extras/c-ares --libdir=$PRIVATELIBDIR --enable-shared || exit 1 ./configure --prefix=$cur_dir/extras/c-ares --libdir=$PRIVATELIBDIR --enable-shared --disable-tests || exit 1
CFLAGS="$save_cflags" CFLAGS="$save_cflags"
AC_MSG_RESULT(compiling c-ares resolver library) AC_MSG_RESULT(compiling c-ares resolver library)
$ac_cv_prog_MAKER || exit 1 $ac_cv_prog_MAKER || exit 1

View File

@ -7,7 +7,7 @@
\___/|_| |_|_| \___|\__,_|_|\___/\_| \_| \____/\__,_| \___/|_| |_|_| \___|\__,_|_|\___/\_| \_| \____/\__,_|
Configuration Program Configuration Program
for UnrealIRCd 5.0.9 for UnrealIRCd 5.2.0.1
This program will help you to compile your IRC server, and ask you This program will help you to compile your IRC server, and ask you
questions regarding the compile-time settings of it during the process. questions regarding the compile-time settings of it during the process.
@ -22,7 +22,7 @@ https://www.unrealircd.org/docs/UnrealIRCd_5_documentation
The full release notes are available in doc/RELEASE-NOTES.md The full release notes are available in doc/RELEASE-NOTES.md
For easier viewing, check out the latest online release notes at: For easier viewing, check out the latest online release notes at:
https://github.com/unrealircd/unrealircd/blob/unreal50/doc/RELEASE-NOTES.md https://github.com/unrealircd/unrealircd/blob/unreal52/doc/RELEASE-NOTES.md
UnrealIRCd 5 is compatible with the following services: UnrealIRCd 5 is compatible with the following services:
* anope with the "unreal4" protocol module - version 2.0.7 or higher required! * anope with the "unreal4" protocol module - version 2.0.7 or higher required!

View File

@ -1,7 +1,176 @@
UnrealIRCd 5.0.9 Release Notes UnrealIRCd 5.2.0.1 Release Notes
=============================== =================================
This release comes with several nice feature enhancements. There are no major bug fixes. About 5.2.0.1
--------------
5.2.0.1 fixes an issue with spamfilter that was present in 5.2.0.
In channels spamfilters were processed for type ```p``` instead of ```c```.
Existing 5.2.0 users on *NIX can upgrade without restart by running
```./unrealircd hot-patch wrongspamfilter520```
UnrealIRCd 5.2.0 is out!
-------------------------
This is UnrealIRCd 5.2.0, a release with lots of new features.
The two main new features are: an improved and more flexible anti-flood block
and channel history which can now be stored encrypted on disk and allows
clients to fetch hundreds/thousands of lines.
Upgrading and the 5.0.x series
-------------------------------
UnrealIRCd 5.2.0 is the direct successor to 5.0.9/5.0.9.1.
There will be [no further 5.0.x releases](https://www.unrealircd.org/docs/FAQ#About_the_new_5.2.x_series),
in particular there will be no 5.0.10.
Only four bugs that affect a limited number of people/networks were fixed.
UnrealIRCd 5.2.0 is mostly a feature release.
Admins wishing to take a conservative approach don't need to rush an
upgrade from 5.0.x to 5.2.0, they can wait for a 5.2.1 or 5.2.2 release.
If you are upgrading from 5.0.9(.1) to 5.2.0 then feel free to try the new
```./unrealircd upgrade``` command.
The only configuration change is in the set::anti-flood block (as explained
further down under *Enhancements*). When starting UnrealIRCd will give you
clear instructions if anything needs to be changed (and what).
This process is really minor, the server will usually tell you to just
delete a few old lines from the configuration file.
Enhancements
-------------
* The set::anti-flood block has been redone so you can have different limits
for *unknown-users* and *known-users*.
* As a reminder, by default, *known-users* are users who are identified
to services OR are on an IP that has been connected for over 2 hours
in the past X days. The exact definition of "known-users" is in the
[security-group block](https://www.unrealircd.org/docs/Security-group_block).
* See [here](https://www.unrealircd.org/docs/Anti-flood_settings)
for more information on the layout of the new set::anti-flood block.
* All violations of target-flood, nick-flood, join-flood, away-flood,
invite-flood, knock-flood, max-concurrent-conversations are now
reported to opers with the snomask ```f``` (flood).
* Add support for database encryption. The way this works
is that you define an encryption password in a
[secret { } block](https://www.unrealircd.org/docs/Secret_block).
Then from the various modules you can refer to this secret
block, from
[set::reputation::db-secret](https://www.unrealircd.org/docs/Set_block#set::reputation),
[set::tkldb::db-secret](https://www.unrealircd.org/docs/Set_block#set::tkldb)
and [set::channeldb::db-secret](https://www.unrealircd.org/docs/Set_block#set::channeldb).
This way you can encrypt the reputation, TKL and channel
database for increased privacy.
* Add optional support for
[persistent channel history](https://www.unrealircd.org/docs/Set_block#Persistent_channel_history):
* This stores channel history on disk for channels that have
both ```+H``` and ```+P``` set.
* If you enable this then we ALWAYS require you to set an
encryption password, as we do not allow storing of
channel history in plain text.
* If you enable the option, then the history is stored in
```data/history/``` in individual .db files. No channel
names are visible in the filenames for optimal privacy.
* See [Persistent channel history](https://www.unrealircd.org/docs/Set_block#Persistent_channel_history)
on how to enable this. By default it is off.
* Add support for IRCv3
[draft/chathistory](https://ircv3.net/specs/extensions/chathistory).
* The maximums for channel mode ```+H``` have been raised and are now
different for ```+r``` (registered) and ```-r``` channels. For unregistered
channels the limit is now 200 lines / 31 days. For registered channels
the limit is 5000 lines / 31 days. The old limit for both was 200 lines / 7 days.
These maximums can be changed in the now slightly different
[set::history::channel::max-storage-per-channel](https://www.unrealircd.org/docs/Set_block#set::history)
block.
* Add c-ares and libsodium version output to boot screen and /VERSION.
* WHOX now supports displaying the
[reputation score](https://www.unrealircd.org/docs/Reputation_score).
If you are an IRCOp then you can use e.g. ```WHO * %cuhsnfmdaRr```.
* Add ability to [spamfilter](https://www.unrealircd.org/docs/Spamfilter)
message tags via the new ```T``` target. Right now it would be unusual
to use this, but some day when we have more
[message tags](https://www.unrealircd.org/docs/Message_tags) it
may come in handy.
* Support [```+draft/reply```](https://ircv3.net/specs/client-tags/reply) IRCv3
client tag. Can be used by bots (and others) to indicate to what message
people are replying to. This module, reply-tag, is loaded by default.
* Send [```draft/bot```](https://ircv3.net/specs/extensions/bot-mode) IRCv3
message tag if the user has mode ```+B``` set.
* [Websockets](https://www.unrealircd.org/docs/WebSocket_support):
add support for clients to negotiate an explicit type via
```Sec-WebSocket-Protocol```, instead of only the default type from
[listen::websocket::type](https://www.unrealircd.org/docs/WebSocket_support#2._Enable_websocket_on_the_port).
This is based on an IRCv3 websocket draft specification.
Note that UnrealIRCd refuses type text if your configuration allows
non-UTF8 characters in channel or nick names because it would lead
to security and compatibility issues.
* [set::restrict-commands](https://www.unrealircd.org/docs/Set_block#set::restrict-commands):
new option *exempt-tls* which allows SSL/TLS users to bypass a restriction.
Fixes
------
* Server squiting the wrong side. Often harmless, but when (re)connecting
rapidly to multiple servers with autoconnect this could cause the
network to fall apart.
* Forbid using [extended server bans](https://www.unrealircd.org/docs/Extended_server_bans)
in ZLINE/GZLINE since they won't work there.
* Extended server ban ```~a:accname``` was not working for shun, and only
partially working for kline/gline.
* More accurate /ELINE error message.
Changed
--------
* Channel mode ```+H``` always showed time in minutes (```m```) until now.
From now on it will show it in minutes (```m```), hours (```h```) or
days (```d```) depending on the actual value. Eg ```+H 50:7d```.
* If you ran ```./unrealircd stop``` we used to wait only 1 second.
From now on we will wait up to 10 seconds max. This gives UnrealIRCd
plenty of time to write database files.
* If you have zero [log blocks](https://www.unrealircd.org/docs/Log_block)
then we already automatically logged errors to ```ircd.log```.
From now on we will log everything (not only errors) to that file.
Removed
--------
* Version check for curl and openssl as nowadays they have ABI guarantees.
Module coders / Developers
---------------------------
* New UnrealDB API and disk format, see
https://www.unrealircd.org/docs/Dev:UnrealDB
* We now use libsodium for file encryption routines as well
as some helpers to lock/clear passwords in memory.
* Updated ```HOOKTYPE_LOCAL_NICKCHANGE``` and
```HOOKTYPE_REMOTE_NICKCHANGE``` to include an
```MessageTag *mtags``` argument in the middle.
You can use ```#if UNREAL_VERSION_TIME>=202115``` to detect this.
* Updated channel mode ```conv_param``` function to
include a ```Channel *channel``` argument at the end.
You can use ```#if UNREAL_VERSION_TIME>=202120``` to detect this.
* New: ```ModuleSetOptions(modinfo->handle, MOD_OPT_UNLOAD_PRIORITY, priority);```.
This can be used for modules to indicate they wish to be unloaded
before or after others. It is used by for example the channel
and history modules so they can save their databases before
channel mode modules or other modules get unloaded.
* New CAP [```draft/chathistory```](https://ircv3.net/specs/extensions/chathistory).
If a client REQ's this CAP then UnrealIRCd won't send history on-join as
it assumes the client will fetch it when they feel the need for it.
* New informative CAP:
[```unrealircd.org/history-backend```](https://www.unrealircd.org/history-backend)
Reminder: UnrealIRCd 4 is no longer supported
----------------------------------------------
UnrealIRCd 4.x is [no longer supported](https://www.unrealircd.org/docs/UnrealIRCd_4_EOL).
Admins must [upgrade to UnrealIRCd 5](https://www.unrealircd.org/docs/Upgrading_from_4.x).
UnrealIRCd 5.0.9.1
-------------------
The only change between 5.0.9 and 5.0.9.1 is:
* Build improvements on *NIX (faster compiling and lower memory requirements)
* Windows version is unchanged and still 5.0.9
UnrealIRCd 5.0.9
-----------------
The 5.0.9 release comes with several nice feature enhancements. There are no major bug fixes.
Enhancements: Enhancements:
* Changes to the "Client connecting" notice on IRC (for IRCOps): * Changes to the "Client connecting" notice on IRC (for IRCOps):
@ -38,7 +207,7 @@ Fixes:
missing. missing.
Changes: Changes:
* Add doc/KEYS which contains the public key(s) used to sign UnrealIRCd releases * Add ```doc/KEYS``` which contains the public key(s) used to sign UnrealIRCd releases
* The options set::anti-flood::unknown-flood-* have been renamed and * The options set::anti-flood::unknown-flood-* have been renamed and
integrated in a new block called integrated in a new block called
[set::anti-flood::handshake-data-flood](https://www.unrealircd.org/docs/Set_block#set::anti-flood::handshake-data-flood). [set::anti-flood::handshake-data-flood](https://www.unrealircd.org/docs/Set_block#set::anti-flood::handshake-data-flood).
@ -49,18 +218,6 @@ That is, when in "auto" mode, which is like for 99% of the users.
Note that the system may still limit the actual number of connections Note that the system may still limit the actual number of connections
to a lower value, epending on the value of ```ulimit -n -H```. to a lower value, epending on the value of ```ulimit -n -H```.
Reminder: UnrealIRCd 4 is no longer supported
----------------------------------------------
UnrealIRCd 4.x is [no longer supported](https://www.unrealircd.org/docs/UnrealIRCd_4_EOL).
Admins must upgrade to UnrealIRCd 5.
Upgrading from 4.x to 5.x?
Then check out the *UnrealIRCd 5* release notes [further down](#unrealircd-5).
Or, at the very least, check out
[Upgrading from 4.x](https://www.unrealircd.org/docs/Upgrading_from_4.x).
UnrealIRCd 5.0.8 UnrealIRCd 5.0.8
----------------- -----------------

View File

@ -162,6 +162,8 @@ loadmodule "extbans/securitygroup"; /* +b ~G */
loadmodule "account-notify"; loadmodule "account-notify";
loadmodule "account-tag"; loadmodule "account-tag";
loadmodule "batch"; loadmodule "batch";
loadmodule "bot-tag";
loadmodule "chathistory";
loadmodule "clienttagdeny"; loadmodule "clienttagdeny";
loadmodule "echo-message"; loadmodule "echo-message";
loadmodule "labeled-response"; loadmodule "labeled-response";
@ -169,6 +171,7 @@ loadmodule "link-security";
loadmodule "message-ids"; loadmodule "message-ids";
loadmodule "message-tags"; loadmodule "message-tags";
loadmodule "plaintext-policy"; loadmodule "plaintext-policy";
loadmodule "reply-tag";
loadmodule "server-time"; loadmodule "server-time";
loadmodule "sts"; loadmodule "sts";
loadmodule "typing-indicator"; loadmodule "typing-indicator";
@ -186,7 +189,7 @@ loadmodule "history_backend_mem";
#loadmodule "history_backend_null"; #loadmodule "history_backend_null";
loadmodule "ident_lookup"; loadmodule "ident_lookup";
loadmodule "jointhrottle"; loadmodule "jointhrottle";
#loadmodule "targetfloodprot"; loadmodule "targetfloodprot";
loadmodule "tkldb"; loadmodule "tkldb";
loadmodule "tls_antidos"; loadmodule "tls_antidos";
loadmodule "userhost-tag"; loadmodule "userhost-tag";

View File

@ -5,7 +5,7 @@ oper acidvegas {
operclass netadmin; operclass netadmin;
require-modes z; require-modes z;
maxlogins 1; maxlogins 1;
vhost super.nets; vhost most.dangerous.motherfuck;
swhois "1,1 1,5 1,1 "; swhois "1,1 1,5 1,1 ";
swhois "1,1 1,5 1,7 1,5 1,7 1,5 1,1 "; swhois "1,1 1,5 1,7 1,5 1,7 1,5 1,1 ";
swhois "1,1 1,5 1,7 1,5 1,7 1,5 1,7 1,5 1,1 0 1 "; swhois "1,1 1,5 1,7 1,5 1,7 1,5 1,7 1,5 1,1 0 1 ";

View File

@ -26,6 +26,10 @@ listen { ip *; port 6667; options { clientsonly; } }
listen { ip *; port 6697; options { clientsonly; tls; } } listen { ip *; port 6697; options { clientsonly; tls; } }
listen { ip *; port REDACTED; options { serversonly; tls; } } listen { ip *; port REDACTED; options { serversonly; tls; } }
deny channel { channel "#pumpcoin"; reason "This channel has moved to #exchange"; redirect "#exchange"; }
deny channel { channel "#dev"; reason "This channel has moved to #superbowl"; redirect "#superbowl"; }
deny channel { channel "#help"; reason "This channel has moved to #superbowl"; redirect "#superbowl"; }
link irc.supernets.org { link irc.supernets.org {
incoming { mask REDACTED; } incoming { mask REDACTED; }
outgoing { outgoing {
@ -106,7 +110,7 @@ set {
private-notice { connect-delay 3600; exempt-identified yes; exempt-reputation-score 100; } private-notice { connect-delay 3600; exempt-identified yes; exempt-reputation-score 100; }
} }
#auto-join "#superbowl"; #auto-join "#superbowl";
oper-auto-join "#help"; oper-auto-join "#superbowl";
static-quit "EMO-QUIT"; static-quit "EMO-QUIT";
static-part "EMO-PART"; static-part "EMO-PART";
who-limit 100; who-limit 100;
@ -141,28 +145,43 @@ set {
oper-message "Network operators must connect using an up-to-date SSL/TLS protocol or cipher"; oper-message "Network operators must connect using an up-to-date SSL/TLS protocol or cipher";
} }
anti-flood { anti-flood {
away-flood 3:300; everyone {
connect-flood 3:300; connect-flood 3:300;
invite-flood 3:300; handshake-data-flood {
join-flood 3:300; amount 4k;
knock-flood 3:300; ban-action gzline;
nick-flood 3:300; ban-time 1h;
max-concurrent-conversations { }
users 5; target-flood {
new-user-every 60s; channel-notice 15:5;
channel-privmsg 45:5;
channel-tagmsg 15:5;
private-notice 10:5;
private-privmsg 30:5;
private-tagmsg 10:5;
}
} }
#target-flood { known-users {
# channel-privmsg 45:5; away-flood 3:300;
# channel-notice 15:5; invite-flood 3:300;
# channel-tagmsg 15:5; join-flood 3:300;
# private-privmsg 30:5; knock-flood 3:300;
# private-notice 10:5; nick-flood 3:300;
# private-tagmsg 10:5; max-concurrent-conversations {
#} users 5;
handshake-data-flood { new-user-every 60s;
amount 4k; }
ban-action gzline; }
ban-time 1h; unknown-users {
away-flood 3:300;
invite-flood 3:300;
join-flood 3:300;
knock-flood 3:300;
nick-flood 3:300;
max-concurrent-conversations {
users 3;
new-user-every 60s;
}
} }
} }
default-bantime 30d; default-bantime 30d;
@ -183,19 +202,22 @@ set {
unauthorized "8,4 E N T E R T H E V O I D "; unauthorized "8,4 E N T E R T H E V O I D ";
} }
antimixedutf8 { antimixedutf8 {
score 10; score 8;
ban-action block; ban-action block;
ban-reason "8,4 E N T E R T H E V O I D "; ban-reason "8,4 E N T E R T H E V O I D ";
} }
connthrottle { connthrottle {
known-users { minimum-reputation-score 24; sasl-bypass yes; } known-users { minimum-reputation-score 100; sasl-bypass yes; }
new-users { local-throttle 20:60; global-throttle 30:60; } new-users { local-throttle 20:60; global-throttle 30:60; }
disabled-when { reputation-gathering 1w; start-delay 3m; } disabled-when { reputation-gathering 1w; start-delay 3m; }
} }
history { history {
channel { channel {
playback-on-join { lines 100; time 1d; } playback-on-join { lines 100; time 1d; }
max-storage-per-channel { lines 100; time 1d; } max-storage-per-channel {
registered { lines 100; time 1d; }
unregistered { lines 50; time 12h; }
}
} }
} }
hide-idle-time { policy usermode; } hide-idle-time { policy usermode; }
@ -206,4 +228,11 @@ hideserver {
disable-links yes; disable-links yes;
map-deny-message "Denied"; map-deny-message "Denied";
links-deny-message "Denied"; links-deny-message "Denied";
}
security-group known-users {
identified yes;
webirc no;
tls no;
reputation-score 100;
} }

View File

@ -40,6 +40,14 @@ else
./run -services atheme || exit 1 ./run -services atheme || exit 1
fi fi
# Database writing/reading tests
## unencrypted:
./run -services none -boot tests/db/writing/* || exit 1
./run -services none -keepdbs -boot tests/db/reading/* || exit 1
## encrypted:
./run -services none -include db_crypted.conf -boot tests/db/writing/* || exit 1
./run -services none -include db_crypted.conf -keepdbs -boot tests/db/reading/* || exit 1
# Do cipherscan test at the end # Do cipherscan test at the end
# Has problems on non-Linux-64-bit, so we skip there: # Has problems on non-Linux-64-bit, so we skip there:
if [ "$FREEBSD" = 0 -a "$HOSTNAME" != "ub18-ia32" ]; then if [ "$FREEBSD" = 0 -a "$HOSTNAME" != "ub18-ia32" ]; then

View File

@ -78,11 +78,26 @@ if %ERRORLEVEL% NEQ 0 EXIT /B 1
cd unrealircd-tests cd unrealircd-tests
dir dir
rem All tests except db:
"C:\Program Files\Git\bin\bash.exe" ./runwin "C:\Program Files\Git\bin\bash.exe" ./runwin
if %ERRORLEVEL% NEQ 0 EXIT /B 1 if %ERRORLEVEL% NEQ 0 EXIT /B 1
rem Test unencrypted db's:
"C:\Program Files\Git\bin\bash.exe" ./runwin -boot tests/db/writing/*
if %ERRORLEVEL% NEQ 0 EXIT /B 1
"C:\Program Files\Git\bin\bash.exe" ./runwin -keepdbs -boot tests/db/reading/*
if %ERRORLEVEL% NEQ 0 EXIT /B 1
rem Test encrypted db's:
"C:\Program Files\Git\bin\bash.exe" ./runwin -include db_crypted.conf -boot tests/db/writing/*
if %ERRORLEVEL% NEQ 0 EXIT /B 1
"C:\Program Files\Git\bin\bash.exe" ./runwin -include db_crypted.conf -keepdbs -boot tests/db/reading/*
if %ERRORLEVEL% NEQ 0 EXIT /B 1
goto end goto end
:installerfailed :installerfailed
type setup.log type setup.log
echo INSTALLATION FAILED echo INSTALLATION FAILED

View File

@ -15,4 +15,7 @@ PCRE2_LIB_DIR="c:\projects\unrealircd-5-libs\pcre2\lib" ^
PCRE2LIB="pcre2-8.lib" ^ PCRE2LIB="pcre2-8.lib" ^
ARGON2_LIB_DIR="c:\projects\unrealircd-5-libs\argon2\vs2015\build" ^ ARGON2_LIB_DIR="c:\projects\unrealircd-5-libs\argon2\vs2015\build" ^
ARGON2_INC_DIR="c:\projects\unrealircd-5-libs\argon2\include" ^ ARGON2_INC_DIR="c:\projects\unrealircd-5-libs\argon2\include" ^
ARGON2LIB="Argon2RefDll.lib" %* ARGON2LIB="Argon2RefDll.lib" ^
SODIUM_LIB_DIR="c:\projects\unrealircd-5-libs\libsodium\bin\x64\Release\v142\dynamic" ^
SODIUM_INC_DIR="c:\projects\unrealircd-5-libs\libsodium\src\libsodium\include" ^
SODIUMLIB="libsodium.lib" %*

View File

@ -90,7 +90,7 @@ cd "$OUTD" || exit 1
echo "Building and installing libcurl" echo "Building and installing libcurl"
CPPFLAGS="-I$ARESPATH/include" ./configure --prefix=$UNREALDIR/extras/curl --libdir=$LIBDIR --enable-shared \ CPPFLAGS="-I$ARESPATH/include" ./configure --prefix=$UNREALDIR/extras/curl --libdir=$LIBDIR --enable-shared \
--disable-thread --enable-ares=$ARESPATH --disable-ipv6 --enable-ares=$ARESPATH --with-openssl
cp -R $ARESPATH/lib ares cp -R $ARESPATH/lib ares
make && make install make && make install

View File

@ -38,7 +38,7 @@ PROJECT_NAME = "UnrealIRCd"
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = 5.0.9 PROJECT_NUMBER = 5.2.0.1
# Using the PROJECT_BRIEF tag one can provide an optional one line description # Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a # for a project that appears at the top of each page and should give viewer a

BIN
extras/libsodium.tar.gz Normal file

Binary file not shown.

View File

@ -1,6 +1,7 @@
/************************************************************************ /************************************************************************
* Unreal Internet Relay Chat Daemon, include/dynconf.h * Unreal Internet Relay Chat Daemon, include/dynconf.h
* Copyright (C) 1999 Carsten Munk * Copyright (C) 1999-2003 Carsten Munk
* Copyright (C) 2003-2021 Bram Matthys
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -22,6 +23,15 @@
#define DYNCONF_H #define DYNCONF_H
typedef struct FloodSettings FloodSettings;
struct FloodSettings {
FloodSettings *prev, *next;
char *name;
int limit[MAXFLOODOPTIONS];
long period[MAXFLOODOPTIONS];
};
typedef struct NetworkConfiguration NetworkConfiguration; typedef struct NetworkConfiguration NetworkConfiguration;
struct NetworkConfiguration { struct NetworkConfiguration {
unsigned x_inah:1; unsigned x_inah:1;
@ -118,16 +128,7 @@ struct Configuration {
int handshake_data_flood_ban_action; int handshake_data_flood_ban_action;
struct ChMode modes_on_join; struct ChMode modes_on_join;
int level_on_join; int level_on_join;
unsigned char away_count; FloodSettings *floodsettings;
long away_period;
unsigned char nick_count;
long nick_period;
unsigned char invite_count;
long invite_period;
unsigned char knock_count;
long knock_period;
unsigned char max_concurrent_conversations_users;
unsigned char max_concurrent_conversations_new_user_every;
int ident_connect_timeout; int ident_connect_timeout;
int ident_read_timeout; int ident_read_timeout;
long default_bantime; long default_bantime;
@ -234,15 +235,6 @@ extern MODVAR int ipv6_disabled;
#define MODES_ON_JOIN iConf.modes_on_join.mode #define MODES_ON_JOIN iConf.modes_on_join.mode
#define LEVEL_ON_JOIN iConf.level_on_join #define LEVEL_ON_JOIN iConf.level_on_join
#define AWAY_PERIOD iConf.away_period
#define AWAY_COUNT iConf.away_count
#define NICK_PERIOD iConf.nick_period
#define NICK_COUNT iConf.nick_count
#define KNOCK_PERIOD iConf.knock_period
#define KNOCK_COUNT iConf.knock_count
#define INVITE_PERIOD iConf.invite_period
#define INVITE_COUNT iConf.invite_count
#define IDENT_CONNECT_TIMEOUT iConf.ident_connect_timeout #define IDENT_CONNECT_TIMEOUT iConf.ident_connect_timeout
#define IDENT_READ_TIMEOUT iConf.ident_read_timeout #define IDENT_READ_TIMEOUT iConf.ident_read_timeout
@ -325,17 +317,8 @@ struct SetCheck {
unsigned has_restrict_channelmodes:1; unsigned has_restrict_channelmodes:1;
unsigned has_restrict_extendedbans:1; unsigned has_restrict_extendedbans:1;
unsigned has_channel_command_prefix:1; unsigned has_channel_command_prefix:1;
unsigned has_anti_flood_handshake_data_flood_amount:1;
unsigned has_anti_flood_handshake_data_flood_ban_action:1;
unsigned has_anti_flood_handshake_data_flood_ban_time:1;
unsigned has_modes_on_join:1; unsigned has_modes_on_join:1;
unsigned has_level_on_join:1; unsigned has_level_on_join:1;
unsigned has_anti_flood_away_count:1;
unsigned has_anti_flood_away_period:1;
unsigned has_anti_flood_nick_flood:1;
unsigned has_anti_flood_connect_flood:1;
unsigned has_anti_flood_invite_flood:1;
unsigned has_anti_flood_knock_flood:1;
unsigned has_ident_connect_timeout:1; unsigned has_ident_connect_timeout:1;
unsigned has_ident_read_timeout:1; unsigned has_ident_read_timeout:1;
unsigned has_default_bantime:1; unsigned has_default_bantime:1;

View File

@ -285,6 +285,7 @@ extern char *myctime(time_t);
extern char *short_date(time_t, char *buf); extern char *short_date(time_t, char *buf);
extern char *long_date(time_t); extern char *long_date(time_t);
extern void exit_client(Client *client, MessageTag *recv_mtags, char *comment); extern void exit_client(Client *client, MessageTag *recv_mtags, char *comment);
extern void exit_client_ex(Client *client, Client *origin, MessageTag *recv_mtags, char *comment);
extern void initstats(), tstats(Client *, char *); extern void initstats(), tstats(Client *, char *);
extern char *check_string(char *); extern char *check_string(char *);
extern char *make_nick_user_host(char *, char *, char *); extern char *make_nick_user_host(char *, char *, char *);
@ -312,7 +313,7 @@ extern int find_str_match_link(Link *, char *);
extern void free_str_list(Link *); extern void free_str_list(Link *);
extern Link *make_link(); extern Link *make_link();
extern Ban *make_ban(); extern Ban *make_ban();
extern ClientUser *make_user(Client *); extern User *make_user(Client *);
extern Server *make_server(); extern Server *make_server();
extern Client *make_client(Client *, Client *); extern Client *make_client(Client *, Client *);
extern Member *find_channel_link(Member *, Channel *); extern Member *find_channel_link(Member *, Channel *);
@ -447,6 +448,7 @@ extern void Auth_FreeAuthConfig(AuthConfig *as);
extern int Auth_Check(Client *cptr, AuthConfig *as, char *para); extern int Auth_Check(Client *cptr, AuthConfig *as, char *para);
extern char *Auth_Hash(int type, char *para); extern char *Auth_Hash(int type, char *para);
extern int Auth_CheckError(ConfigEntry *ce); extern int Auth_CheckError(ConfigEntry *ce);
extern int Auth_AutoDetectHashType(char *hash);
extern void make_cloakedhost(Client *client, char *curr, char *buf, size_t buflen); extern void make_cloakedhost(Client *client, char *curr, char *buf, size_t buflen);
extern int channel_canjoin(Client *client, char *name); extern int channel_canjoin(Client *client, char *name);
@ -545,9 +547,31 @@ extern void *safe_alloc(size_t size);
*/ */
#define raw_strldup(str, max) our_strldup(str, max) #define raw_strldup(str, max) our_strldup(str, max)
extern void *safe_alloc_sensitive(size_t size);
/** Free previously allocate memory pointer - this is the sensitive version which
* may ONLY be called on allocations returned by safe_alloc_sensitive() / safe_strdup_sensitive().
* This will set the memory to all zeroes before actually deallocating.
* It also sets the pointer to NULL, since that would otherwise be common to forget.
* @note If you call this function on normally allocated memory (non-sensitive) then we will crash.
*/
#define safe_free_sensitive(x) do { if (x) sodium_free(x); x = NULL; } while(0)
/** Free previous memory (if any) and then save a duplicate of the specified string -
* This is the 'sensitive' version which should only be used for HIGHLY sensitive data,
* as it wastes about 8000 bytes even if you only duplicate a string of 32 bytes (this is by design).
* @param dst The current pointer and the pointer where a new copy of the string will be stored.
* @param str The string you want to copy
*/
#define safe_strdup_sensitive(dst,str) do { if (dst) sodium_free(dst); if (!(str)) dst = NULL; else dst = our_strdup_sensitive(str); } while(0)
/** Safely destroy a string in memory (but do not free!) */
#define destroy_string(str) sodium_memzero(str, strlen(str))
/** @} */ /** @} */
extern char *our_strdup(const char *str); extern char *our_strdup(const char *str);
extern char *our_strldup(const char *str, size_t max); extern char *our_strldup(const char *str, size_t max);
extern char *our_strdup_sensitive(const char *str);
extern long config_checkval(char *value, unsigned short flags); extern long config_checkval(char *value, unsigned short flags);
extern void config_status(FORMAT_STRING(const char *format), ...) __attribute__((format(printf,1,2))); extern void config_status(FORMAT_STRING(const char *format), ...) __attribute__((format(printf,1,2)));
@ -701,7 +725,8 @@ extern MODVAR void (*tkl_stats)(Client *cptr, int type, char *para, int *cnt);
extern MODVAR void (*tkl_sync)(Client *client); extern MODVAR void (*tkl_sync)(Client *client);
extern MODVAR void (*cmd_tkl)(Client *client, MessageTag *recv_mtags, int parc, char *parv[]); extern MODVAR void (*cmd_tkl)(Client *client, MessageTag *recv_mtags, int parc, char *parv[]);
extern MODVAR int (*place_host_ban)(Client *client, BanAction action, char *reason, long duration); extern MODVAR int (*place_host_ban)(Client *client, BanAction action, char *reason, long duration);
extern MODVAR int (*match_spamfilter)(Client *client, char *str_in, int type, char *target, int flags, TKL **rettk); extern MODVAR int (*match_spamfilter)(Client *client, char *str_in, int type, char *cmd, char *target, int flags, TKL **rettk);
extern MODVAR int (*match_spamfilter_mtags)(Client *client, MessageTag *mtags, char *cmd);
extern MODVAR int (*join_viruschan)(Client *client, TKL *tk, int type); extern MODVAR int (*join_viruschan)(Client *client, TKL *tk, int type);
extern MODVAR unsigned char *(*StripColors)(unsigned char *text); extern MODVAR unsigned char *(*StripColors)(unsigned char *text);
extern MODVAR const char *(*StripControlCodes)(unsigned char *text); extern MODVAR const char *(*StripControlCodes)(unsigned char *text);
@ -787,6 +812,7 @@ extern char *clean_ban_mask(char *, int, Client *);
extern int find_invex(Channel *channel, Client *client); extern int find_invex(Channel *channel, Client *client);
extern void DoMD5(char *mdout, const char *src, unsigned long n); extern void DoMD5(char *mdout, const char *src, unsigned long n);
extern char *md5hash(char *dst, const char *src, unsigned long n); extern char *md5hash(char *dst, const char *src, unsigned long n);
extern char *sha256hash(char *dst, const char *src, unsigned long n);
extern MODVAR TKL *tklines[TKLISTLEN]; extern MODVAR TKL *tklines[TKLISTLEN];
extern MODVAR TKL *tklines_ip_hash[TKLIPHASHLEN1][TKLIPHASHLEN2]; extern MODVAR TKL *tklines_ip_hash[TKLIPHASHLEN1][TKLIPHASHLEN2];
extern char *cmdname_by_spamftarget(int target); extern char *cmdname_by_spamftarget(int target);
@ -944,8 +970,12 @@ extern void free_message_tags(MessageTag *m);
extern time_t server_time_to_unix_time(const char *tbuf); extern time_t server_time_to_unix_time(const char *tbuf);
extern int history_set_limit(char *object, int max_lines, long max_t); extern int history_set_limit(char *object, int max_lines, long max_t);
extern int history_add(char *object, MessageTag *mtags, char *line); extern int history_add(char *object, MessageTag *mtags, char *line);
extern int history_request(Client *acptr, char *object, HistoryFilter *filter); extern HistoryResult *history_request(char *object, HistoryFilter *filter);
extern int history_destroy(char *object); extern int history_destroy(char *object);
extern int can_receive_history(Client *client);
extern void history_send_result(Client *client, HistoryResult *r);
extern void free_history_result(HistoryResult *r);
extern void free_history_filter(HistoryFilter *f);
extern void special_delayed_unloading(void); extern void special_delayed_unloading(void);
extern int write_int64(FILE *fd, uint64_t t); extern int write_int64(FILE *fd, uint64_t t);
extern int write_int32(FILE *fd, uint32_t t); extern int write_int32(FILE *fd, uint32_t t);
@ -1004,3 +1034,41 @@ extern NameValuePrioList *find_nvplist(NameValuePrioList *list, char *name);
extern void free_nvplist(NameValuePrioList *lst); extern void free_nvplist(NameValuePrioList *lst);
extern char *get_connect_extinfo(Client *client); extern char *get_connect_extinfo(Client *client);
extern char *unreal_strftime(char *str); extern char *unreal_strftime(char *str);
extern void strtolower_safe(char *dst, char *src, int size);
extern int running_interactively(void);
extern void skip_whitespace(char **p);
extern void read_until(char **p, char *stopchars);
/* src/unrealdb.c start */
extern UnrealDB *unrealdb_open(const char *filename, UnrealDBMode mode, char *secret_block);
extern int unrealdb_close(UnrealDB *c);
extern char *unrealdb_test_db(const char *filename, char *secret_block);
extern int unrealdb_write_int64(UnrealDB *c, uint64_t t);
extern int unrealdb_write_int32(UnrealDB *c, uint32_t t);
extern int unrealdb_write_int16(UnrealDB *c, uint16_t t);
extern int unrealdb_write_str(UnrealDB *c, char *x);
extern int unrealdb_write_char(UnrealDB *c, char t);
extern int unrealdb_read_int64(UnrealDB *c, uint64_t *t);
extern int unrealdb_read_int32(UnrealDB *c, uint32_t *t);
extern int unrealdb_read_int16(UnrealDB *c, uint16_t *t);
extern int unrealdb_read_str(UnrealDB *c, char **x);
extern int unrealdb_read_char(UnrealDB *c, char *t);
extern char *unrealdb_test_secret(char *name);
extern UnrealDBConfig *unrealdb_copy_config(UnrealDBConfig *src);
extern UnrealDBConfig *unrealdb_get_config(UnrealDB *db);
extern void unrealdb_free_config(UnrealDBConfig *c);
extern UnrealDBError unrealdb_get_error_code(void);
extern char *unrealdb_get_error_string(void);
/* src/unrealdb.c end */
/* secret { } related stuff */
extern Secret *find_secret(char *secret_name);
extern void free_secret_cache(SecretCache *c);
extern void free_secret(Secret *s);
extern Secret *secrets;
/* end */
extern int check_password_strength(char *pass, int min_length, int strict, char **err);
extern int valid_secret_password(char *pass, char **err);
extern int flood_limit_exceeded(Client *client, FloodOption opt);
extern FloodSettings *find_floodsettings_block(const char *name);
extern FloodSettings *get_floodsettings_for_user(Client *client, FloodOption opt);
extern MODVAR char *floodoption_names[];
extern void flood_limit_exceeded_log(Client *client, char *floodname);

View File

@ -241,14 +241,15 @@ typedef struct {
* *
* This function pointer is NULL (unused) for modes without parameters. * This function pointer is NULL (unused) for modes without parameters.
* @param para The input parameter. * @param para The input parameter.
* @param client The client that the mode request came from (can be NULL) * @param client The client that the mode request came from (can be NULL!)
* @param channel The channel that the mode request came from (can be NULL!)
* @returns pointer to output string (temporary storage) * @returns pointer to output string (temporary storage)
* @note The 'client' field will be NULL if for example called for set::modes-on-join. * @note The 'client' field will be NULL if for example called for set::modes-on-join.
* @note You should probably not use 'client' in most cases. * @note You should probably not use 'client' or 'channel' in most cases.
* In particular you MUST NOT SEND ERRORS to the client. * In particular you MUST NOT SEND ERRORS to the client.
* This should be done in is_ok() and not in conv_param(). * This should be done in is_ok() and not in conv_param().
*/ */
char *(*conv_param)(char *para, Client *client); char *(*conv_param)(char *para, Client *client, Channel *channel);
/** Free and remove parameter from list. /** Free and remove parameter from list.
* This function pointer is NULL (unused) for modes without parameters. * This function pointer is NULL (unused) for modes without parameters.
@ -309,7 +310,7 @@ typedef struct {
int (*is_ok)(Client *,Channel *, char mode, char *para, int, int); int (*is_ok)(Client *,Channel *, char mode, char *para, int, int);
void * (*put_param)(void *, char *); void * (*put_param)(void *, char *);
char * (*get_param)(void *); char * (*get_param)(void *);
char * (*conv_param)(char *, Client *); char * (*conv_param)(char *, Client *, Channel *);
void (*free_param)(void *); void (*free_param)(void *);
void * (*dup_struct)(void *); void * (*dup_struct)(void *);
int (*sjoin_check)(Channel *, void *, void *); int (*sjoin_check)(Channel *, void *, void *);
@ -486,11 +487,43 @@ typedef struct {
/** @} */ /** @} */
/** Filter for history: the command / type of the request */
typedef enum HistoryFilterCommand {
HFC_SIMPLE=1, /**< Simple history request for lines / unixtime */
HFC_BEFORE=2, /**< CHATHISTORY BEFORE */
HFC_AFTER=3, /**< CHATHISTORY AFTER */
HFC_LATEST=4, /**< CHATHISTORY LATEST */
HFC_AROUND=5, /**< CHATHISTORY AROUND */
HFC_BETWEEN=6 /**< CHATHISTORY BETWEEN */
} HistoryFilterCommand;
/** Filter for history get requests */ /** Filter for history get requests */
typedef struct HistoryFilter HistoryFilter; typedef struct HistoryFilter HistoryFilter;
struct HistoryFilter { struct HistoryFilter {
int last_lines; HistoryFilterCommand cmd; /**< Filter command, one of HistoryFilterCommand */
int last_seconds; int last_lines; /**< Used by HFC_SIMPLE */
int last_seconds; /**< Used by HFC_SIMPLE */
char *timestamp_a; /**< First parameter of HFC_* (either this or msgid_a) */
char *msgid_a; /**< First parameter of HFC_* (either this or timestamp_a) */
char *timestamp_b; /**< Second parameter of HFC_BETWEEN (either this or msgid_b) */
char *msgid_b; /**< Second parameter of HFC_BETWEEN (either this or timestamp_b) */
int limit; /**< Maximum number of lines to return */
};
/** History log lines, used by HistoryResult among others */
typedef struct HistoryLogLine HistoryLogLine;
struct HistoryLogLine {
HistoryLogLine *prev, *next;
time_t t;
MessageTag *mtags;
char line[1];
};
typedef struct HistoryResult HistoryResult;
struct HistoryResult {
char *object; /**< Name of the history object, eg '#test' */
HistoryLogLine *log; /**< The resulting log lines */
HistoryLogLine *log_tail; /**< Last entry in the log lines */
}; };
/** History Backend */ /** History Backend */
@ -500,7 +533,7 @@ struct HistoryBackend {
char *name; /**< The name of the history backend (eg: "mem") */ char *name; /**< The name of the history backend (eg: "mem") */
int (*history_set_limit)(char *object, int max_lines, long max_time); /**< Impose a limit on a history object */ int (*history_set_limit)(char *object, int max_lines, long max_time); /**< Impose a limit on a history object */
int (*history_add)(char *object, MessageTag *mtags, char *line); /**< Add to history */ int (*history_add)(char *object, MessageTag *mtags, char *line); /**< Add to history */
int (*history_request)(Client *acptr, char *object, HistoryFilter *filter); /**< Request history */ HistoryResult *(*history_request)(char *object, HistoryFilter *filter); /**< Request history */
int (*history_destroy)(char *object); /**< Destroy history of this object completely */ int (*history_destroy)(char *object); /**< Destroy history of this object completely */
Module *owner; /**< Module introducing this */ Module *owner; /**< Module introducing this */
char unloaded; /**< Internal flag to indicate module is being unloaded */ char unloaded; /**< Internal flag to indicate module is being unloaded */
@ -513,7 +546,7 @@ typedef struct {
char *name; char *name;
int (*history_set_limit)(char *object, int max_lines, long max_time); int (*history_set_limit)(char *object, int max_lines, long max_time);
int (*history_add)(char *object, MessageTag *mtags, char *line); int (*history_add)(char *object, MessageTag *mtags, char *line);
int (*history_request)(Client *acptr, char *object, HistoryFilter *filter); HistoryResult *(*history_request)(char *object, HistoryFilter *filter);
int (*history_destroy)(char *object); int (*history_destroy)(char *object);
} HistoryBackendInfo; } HistoryBackendInfo;
@ -614,11 +647,12 @@ typedef struct ModuleObject {
extern unsigned int ModuleGetError(Module *module); extern unsigned int ModuleGetError(Module *module);
extern const char *ModuleGetErrorStr(Module *module); extern const char *ModuleGetErrorStr(Module *module);
extern unsigned int ModuleGetOptions(Module *module); extern unsigned int ModuleGetOptions(Module *module);
extern unsigned int ModuleSetOptions(Module *module, unsigned int options, int action); extern void ModuleSetOptions(Module *module, unsigned int options, int action);
struct Module struct Module
{ {
struct Module *prev, *next; struct Module *prev, *next;
int priority;
ModuleHeader *header; /* The module's header */ ModuleHeader *header; /* The module's header */
#ifdef _WIN32 #ifdef _WIN32
HMODULE dll; /* Return value of LoadLibrary */ HMODULE dll; /* Return value of LoadLibrary */
@ -644,6 +678,7 @@ struct Module
#define MOD_OPT_OFFICIAL 0x0002 /* Official module, do not set "tainted" */ #define MOD_OPT_OFFICIAL 0x0002 /* Official module, do not set "tainted" */
#define MOD_OPT_PERM_RELOADABLE 0x0004 /* Module is semi-permanent: it can be re-loaded but not un-loaded */ #define MOD_OPT_PERM_RELOADABLE 0x0004 /* Module is semi-permanent: it can be re-loaded but not un-loaded */
#define MOD_OPT_GLOBAL 0x0008 /* Module is required to be loaded globally (i.e. across the entire network) */ #define MOD_OPT_GLOBAL 0x0008 /* Module is required to be loaded globally (i.e. across the entire network) */
#define MOD_OPT_UNLOAD_PRIORITY 0x1000 /* Module wants a higher or lower unload priority */
#define MOD_Dep(name, container,module) {#name, (vFP *) &container, module} #define MOD_Dep(name, container,module) {#name, (vFP *) &container, module}
/** Event structs */ /** Event structs */
@ -1223,17 +1258,19 @@ int hooktype_server_quit(Client *client, MessageTag *mtags);
/** Called when a local user changes the nick name (function prototype for HOOKTYPE_LOCAL_NICKCHANGE). /** Called when a local user changes the nick name (function prototype for HOOKTYPE_LOCAL_NICKCHANGE).
* @param client The client * @param client The client
* @param mtags Message tags associated with the event
* @param newnick The new nick name * @param newnick The new nick name
* @return The return value is ignored (use return 0) * @return The return value is ignored (use return 0)
*/ */
int hooktype_local_nickchange(Client *client, char *newnick); int hooktype_local_nickchange(Client *client, MessageTag *mtags, char *newnick);
/** Called when a remote user changes the nick name (function prototype for HOOKTYPE_REMOTE_NICKCHANGE). /** Called when a remote user changes the nick name (function prototype for HOOKTYPE_REMOTE_NICKCHANGE).
* @param client The client * @param client The client
* @param mtags Message tags associated with the event
* @param newnick The new nick name * @param newnick The new nick name
* @return The return value is ignored (use return 0) * @return The return value is ignored (use return 0)
*/ */
int hooktype_remote_nickchange(Client *client, char *newnick); int hooktype_remote_nickchange(Client *client, MessageTag *mtags, char *newnick);
/** Called when a user wants to join a channel, may the user join? (function prototype for HOOKTYPE_CAN_JOIN). /** Called when a user wants to join a channel, may the user join? (function prototype for HOOKTYPE_CAN_JOIN).
* @param client The client * @param client The client
@ -2222,8 +2259,9 @@ enum EfunctionType {
EFUNC_TKL_SYNCH, EFUNC_TKL_SYNCH,
EFUNC_CMD_TKL, EFUNC_CMD_TKL,
EFUNC_PLACE_HOST_BAN, EFUNC_PLACE_HOST_BAN,
EFUNC_DOSPAMFILTER, EFUNC_MATCH_SPAMFILTER,
EFUNC_DOSPAMFILTER_VIRUSCHAN, EFUNC_MATCH_SPAMFILTER_MTAGS,
EFUNC_JOIN_VIRUSCHAN,
EFUNC_FIND_TKLINE_MATCH_ZAP_EX, EFUNC_FIND_TKLINE_MATCH_ZAP_EX,
EFUNC_SEND_LIST, EFUNC_SEND_LIST,
EFUNC_STRIPCOLORS, EFUNC_STRIPCOLORS,
@ -2308,6 +2346,7 @@ enum EfunctionType {
#define CONFIG_REQUIRE 9 #define CONFIG_REQUIRE 9
#define CONFIG_LISTEN 10 #define CONFIG_LISTEN 10
#define CONFIG_LISTEN_OPTIONS 11 #define CONFIG_LISTEN_OPTIONS 11
#define CONFIG_SET_HISTORY_CHANNEL 12
#define MOD_HEADER Mod_Header #define MOD_HEADER Mod_Header
#define MOD_TEST() DLLFUNC int Mod_Test(ModuleInfo *modinfo) #define MOD_TEST() DLLFUNC int Mod_Test(ModuleInfo *modinfo)

View File

@ -142,6 +142,12 @@
/* Define the location of the modules */ /* Define the location of the modules */
#undef MODULESDIR #undef MODULESDIR
/* machine is bigendian */
#undef NATIVE_BIG_ENDIAN
/* machine is littleendian */
#undef NATIVE_LITTLE_ENDIAN
/* Set to the nickname history length you want */ /* Set to the nickname history length you want */
#undef NICKNAMEHISTORYLENGTH #undef NICKNAMEHISTORYLENGTH
@ -226,3 +232,15 @@
/* Define if you have libcurl installed to get remote includes and MOTD /* Define if you have libcurl installed to get remote includes and MOTD
support */ support */
#undef USE_LIBCURL #undef USE_LIBCURL
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
significant byte first (like Motorola and SPARC, unlike Intel). */
#if defined AC_APPLE_UNIVERSAL_BUILD
# if defined __BIG_ENDIAN__
# define WORDS_BIGENDIAN 1
# endif
#else
# ifndef WORDS_BIGENDIAN
# undef WORDS_BIGENDIAN
# endif
#endif

View File

@ -109,6 +109,7 @@ typedef struct ConfigItem_blacklist_module ConfigItem_blacklist_module;
typedef struct ConfigItem_help ConfigItem_help; typedef struct ConfigItem_help ConfigItem_help;
typedef struct ConfigItem_offchans ConfigItem_offchans; typedef struct ConfigItem_offchans ConfigItem_offchans;
typedef struct SecurityGroup SecurityGroup; typedef struct SecurityGroup SecurityGroup;
typedef struct Secret Secret;
typedef struct ListStruct ListStruct; typedef struct ListStruct ListStruct;
typedef struct ListStructPrio ListStructPrio; typedef struct ListStructPrio ListStructPrio;
@ -120,7 +121,7 @@ typedef struct Watch Watch;
typedef struct Client Client; typedef struct Client Client;
typedef struct LocalClient LocalClient; typedef struct LocalClient LocalClient;
typedef struct Channel Channel; typedef struct Channel Channel;
typedef struct User ClientUser; typedef struct User User;
typedef struct Server Server; typedef struct Server Server;
typedef struct Link Link; typedef struct Link Link;
typedef struct Ban Ban; typedef struct Ban Ban;
@ -211,7 +212,7 @@ typedef OperPermission (*OperClassEntryEvalCallback)(OperClassACLEntryVar* varia
#define LOG_CHGCMDS 0x0100 #define LOG_CHGCMDS 0x0100
#define LOG_OVERRIDE 0x0200 #define LOG_OVERRIDE 0x0200
#define LOG_SPAMFILTER 0x0400 #define LOG_SPAMFILTER 0x0400
#define LOG_DBG 0x0800 /* fixme */ #define LOG_FLOOD 0x0800
/* /*
** 'offsetof' is defined in ANSI-C. The following definition ** 'offsetof' is defined in ANSI-C. The following definition
@ -240,6 +241,14 @@ typedef OperPermission (*OperClassEntryEvalCallback)(OperClassACLEntryVar* varia
*/ */
#define SIPHASH_KEY_LENGTH 16 #define SIPHASH_KEY_LENGTH 16
/** The length of a standard 'msgid' tag (note that special
* msgid tags will be longer).
* The 22 alphanumeric characters provide slightly more
* than 128 bits of randomness (62^22 > 2^128).
* See mtag_add_or_inherit_msgid() for more information.
*/
#define MSGIDLEN 22
/** This specifies the current client status or the client type - see @link ClientStatus @endlink in particular. /** This specifies the current client status or the client type - see @link ClientStatus @endlink in particular.
* You may think "server" or "client" are the only choices here, but there are many more * You may think "server" or "client" are the only choices here, but there are many more
* such as states where the user is in the middle of an SSL/TLS handshake. * such as states where the user is in the middle of an SSL/TLS handshake.
@ -661,6 +670,7 @@ struct ListStructPrio {
CHECK_PRIO_LIST_ENTRY(list) \ CHECK_PRIO_LIST_ENTRY(list) \
CHECK_PRIO_LIST_ENTRY(item) \ CHECK_PRIO_LIST_ENTRY(item) \
CHECK_NULL_LIST_ITEM(item) \ CHECK_NULL_LIST_ITEM(item) \
item->priority = prio; \
add_ListItemPrio((ListStructPrio *)item, (ListStructPrio **)&list, prio); \ add_ListItemPrio((ListStructPrio *)item, (ListStructPrio **)&list, prio); \
} while(0) } while(0)
@ -754,6 +764,7 @@ struct LoopStruct {
unsigned do_bancheck_spamf_user : 1; /* perform 'user' spamfilter bancheck */ unsigned do_bancheck_spamf_user : 1; /* perform 'user' spamfilter bancheck */
unsigned do_bancheck_spamf_away : 1; /* perform 'away' spamfilter bancheck */ unsigned do_bancheck_spamf_away : 1; /* perform 'away' spamfilter bancheck */
unsigned ircd_rehashing : 1; unsigned ircd_rehashing : 1;
unsigned ircd_terminating : 1;
unsigned tainted : 1; unsigned tainted : 1;
Client *rehash_save_cptr, *rehash_save_client; Client *rehash_save_cptr, *rehash_save_client;
int rehash_save_sig; int rehash_save_sig;
@ -862,6 +873,97 @@ typedef void (*CmdFunc)(Client *client, MessageTag *mtags, int parc, char *parv[
typedef void (*AliasCmdFunc)(Client *client, MessageTag *mtags, int parc, char *parv[], char *cmd); typedef void (*AliasCmdFunc)(Client *client, MessageTag *mtags, int parc, char *parv[], char *cmd);
typedef void (*OverrideCmdFunc)(CommandOverride *ovr, Client *client, MessageTag *mtags, int parc, char *parv[]); typedef void (*OverrideCmdFunc)(CommandOverride *ovr, Client *client, MessageTag *mtags, int parc, char *parv[]);
#include <sodium.h>
/* This is the 'chunk size', the size of encryption blocks.
* We choose 4K here since that is a decent amount as of 2021 and
* more would not benefit performance anyway.
* Note that you cannot change this value easily afterwards
* (you cannot read files with a different chunk size).
*/
#define UNREALDB_CRYPT_FILE_CHUNK_SIZE 4096
/** The salt length. Don't change. */
#define UNREALDB_SALT_LEN 16
/** Database modes of operation (read or write)
* @ingroup UnrealDBFunctions
*/
typedef enum UnrealDBMode {
UNREALDB_MODE_READ = 0,
UNREALDB_MODE_WRITE = 1
} UnrealDBMode;
typedef enum UnrealDBCipher {
UNREALDB_CIPHER_XCHACHA20 = 0x0001
} UnrealDBCipher;
typedef enum UnrealDBKDF {
UNREALDB_KDF_ARGON2ID = 0x0001
} UnrealDBKDF;
/** Database configuration for a particular file */
typedef struct UnrealDBConfig {
uint16_t kdf; /**< Key derivation function (always 0x01) */
uint16_t t_cost; /**< Time cost (number of rounds) */
uint16_t m_cost; /**< Memory cost (in number of bitshifts, eg 15 means 1<<15=32M) */
uint16_t p_cost; /**< Parallel cost (number of concurrent threads) */
uint16_t saltlen; /**< Length of the salt (normally UNREALDB_SALT_LEN) */
char *salt; /**< Salt */
uint16_t cipher; /**< Encryption cipher (always 0x01) */
uint16_t keylen; /**< Key length */
char *key; /**< The key used for encryption/decryption */
} UnrealDBConfig;
/** Error codes returned by @ref UnrealDBFunctions
* @ingroup UnrealDBFunctions
*/
typedef enum UnrealDBError {
UNREALDB_ERROR_SUCCESS = 0, /**< Success, not an error */
UNREALDB_ERROR_FILENOTFOUND = 1, /**< File does not exist */
UNREALDB_ERROR_CRYPTED = 2, /**< File is crypted but no password provided */
UNREALDB_ERROR_NOTCRYPTED = 3, /**< File is not crypted and a password was provided */
UNREALDB_ERROR_HEADER = 4, /**< Header is corrupt, invalid or unknown format */
UNREALDB_ERROR_SECRET = 5, /**< Invalid secret { } block provided - either does not exist or does not meet requirements */
UNREALDB_ERROR_PASSWORD = 6, /**< Invalid password provided */
UNREALDB_ERROR_IO = 7, /**< I/O error */
UNREALDB_ERROR_API = 8, /**< API call violation, eg requesting to write on a file opened for reading */
UNREALDB_ERROR_INTERNAL = 9, /**< Internal error, eg crypto routine returned something unexpected */
} UnrealDBError;
/** Database handle
* This is returned by unrealdb_open() and used by all other @ref UnrealDBFunctions
* @ingroup UnrealDBFunctions
*/
typedef struct UnrealDB {
FILE *fd; /**< File descriptor */
UnrealDBMode mode; /**< UNREALDB_MODE_READ / UNREALDB_MODE_WRITE */
int crypted; /**< Are we doing any encryption or just plaintext? */
uint64_t creationtime; /**< When this file was created/updates */
crypto_secretstream_xchacha20poly1305_state st; /**< Internal state for crypto engine */
char buf[UNREALDB_CRYPT_FILE_CHUNK_SIZE]; /**< Buffer used for reading/writing */
int buflen; /**< Length of current data in buffer */
UnrealDBError error_code; /**< Last error code. Whenever this happens we will set this, never overwrite, and block further I/O */
char *error_string; /**< Error string upon failure */
UnrealDBConfig *config; /**< Config */
} UnrealDB;
/** Used for speeding up reading/writing of DBs (so we don't have to run argon2 repeatedly) */
typedef struct SecretCache SecretCache;
struct SecretCache {
SecretCache *prev, *next;
UnrealDBConfig *config;
time_t cache_hit;
};
/** Used for storing secret { } blocks */
struct Secret {
Secret *prev, *next;
char *name;
char *password;
SecretCache *cache;
};
/* tkl: /* tkl:
* TKL_KILL|TKL_GLOBAL = Global K-Line (GLINE) * TKL_KILL|TKL_GLOBAL = Global K-Line (GLINE)
@ -898,12 +1000,13 @@ typedef void (*OverrideCmdFunc)(CommandOverride *ovr, Client *client, MessageTag
#define SPAMF_USERMSG 0x0002 /* p */ #define SPAMF_USERMSG 0x0002 /* p */
#define SPAMF_USERNOTICE 0x0004 /* n */ #define SPAMF_USERNOTICE 0x0004 /* n */
#define SPAMF_CHANNOTICE 0x0008 /* N */ #define SPAMF_CHANNOTICE 0x0008 /* N */
#define SPAMF_PART 0x0010 /* P */ #define SPAMF_PART 0x0010 /* P */
#define SPAMF_QUIT 0x0020 /* q */ #define SPAMF_QUIT 0x0020 /* q */
#define SPAMF_DCC 0x0040 /* d */ #define SPAMF_DCC 0x0040 /* d */
#define SPAMF_USER 0x0080 /* u */ #define SPAMF_USER 0x0080 /* u */
#define SPAMF_AWAY 0x0100 /* a */ #define SPAMF_AWAY 0x0100 /* a */
#define SPAMF_TOPIC 0x0200 /* t */ #define SPAMF_TOPIC 0x0200 /* t */
#define SPAMF_MTAG 0x0400 /* m */
/* Other flags only for function calls: */ /* Other flags only for function calls: */
#define SPAMFLAG_NOWARN 0x0001 #define SPAMFLAG_NOWARN 0x0001
@ -1107,6 +1210,25 @@ extern void unload_all_unused_moddata(void);
#define TLSFLAG_NOSTARTTLS 0x8 #define TLSFLAG_NOSTARTTLS 0x8
#define TLSFLAG_DISABLECLIENTCERT 0x10 #define TLSFLAG_DISABLECLIENTCERT 0x10
/** Flood counters for local clients */
typedef struct FloodCounter {
int count;
long t;
} FloodCounter;
/** This is the list of different flood counters that we keep for local clients. */
/* IMPORTANT: If you change this, update floodoption_names[] in src/user.c too !!!!!!!!!!!! */
typedef enum FloodOption {
FLD_NICK = 0, /**< nick-flood */
FLD_JOIN = 1, /**< join-flood */
FLD_AWAY = 2, /**< away-flood */
FLD_INVITE = 3, /**< invite-flood */
FLD_KNOCK = 4, /**< knock-flood */
FLD_CONVERSATIONS = 5, /**< max-concurrent-conversations */
} FloodOption;
#define MAXFLOODOPTIONS 10
/** This shows the Client struct (any client), the User struct (a user), Server (a server) that are commonly accessed both in the core and by 3rd party coders. /** This shows the Client struct (any client), the User struct (a user), Server (a server) that are commonly accessed both in the core and by 3rd party coders.
* @defgroup CommonStructs Common structs * @defgroup CommonStructs Common structs
* @{ * @{
@ -1119,7 +1241,7 @@ struct Client {
struct list_head lclient_node; /**< For local client list (lclient_list) */ struct list_head lclient_node; /**< For local client list (lclient_list) */
struct list_head special_node; /**< For special lists (server || unknown || oper) */ struct list_head special_node; /**< For special lists (server || unknown || oper) */
LocalClient *local; /**< Additional information regarding locally connected clients */ LocalClient *local; /**< Additional information regarding locally connected clients */
ClientUser *user; /**< Additional information, if this client is a user */ User *user; /**< Additional information, if this client is a user */
Server *serv; /**< Additional information, if this is a server */ Server *serv; /**< Additional information, if this is a server */
ClientStatus status; /**< Client status, one of CLIENT_STATUS_* */ ClientStatus status; /**< Client status, one of CLIENT_STATUS_* */
struct list_head client_hash; /**< For name hash table (clientTable) */ struct list_head client_hash; /**< For name hash table (clientTable) */
@ -1186,6 +1308,7 @@ struct LocalClient {
struct hostent *hostp; /**< Host record for this client (used by DNS code) */ struct hostent *hostp; /**< Host record for this client (used by DNS code) */
char sockhost[HOSTLEN + 1]; /**< Hostname from the socket */ char sockhost[HOSTLEN + 1]; /**< Hostname from the socket */
u_short port; /**< Remote TCP port of client */ u_short port; /**< Remote TCP port of client */
FloodCounter flood[MAXFLOODOPTIONS];
}; };
/** User information (persons, not servers), you use client->user to access these (see also @link Client @endlink). /** User information (persons, not servers), you use client->user to access these (see also @link Client @endlink).
@ -1208,11 +1331,9 @@ struct User {
char *operlogin; /**< Which oper { } block was used to oper up, otherwise NULL - used by oper::maxlogins */ char *operlogin; /**< Which oper { } block was used to oper up, otherwise NULL - used by oper::maxlogins */
struct { struct {
time_t nick_t; /**< For set::anti-flood::nick-flood: time */ time_t nick_t; /**< For set::anti-flood::nick-flood: time */
time_t away_t; /**< For set::anti-flood::away-flood: time */
time_t knock_t; /**< For set::anti-flood::knock-flood: time */ time_t knock_t; /**< For set::anti-flood::knock-flood: time */
time_t invite_t; /**< For set::anti-flood::invite-flood: time */ time_t invite_t; /**< For set::anti-flood::invite-flood: time */
unsigned char nick_c; /**< For set::anti-flood::nick-flood: counter */ unsigned char nick_c; /**< For set::anti-flood::nick-flood: counter */
unsigned char away_c; /**< For set::anti-flood::away-flood: counter */
unsigned char knock_c; /**< For set::anti-flood::knock-flood: counter */ unsigned char knock_c; /**< For set::anti-flood::knock-flood: counter */
unsigned char invite_c; /**< For set::anti-flood::invite-flood: counter */ unsigned char invite_c; /**< For set::anti-flood::invite-flood: counter */
} flood; /**< Anti-flood counters */ } flood; /**< Anti-flood counters */

View File

@ -224,4 +224,15 @@ extern char OSName[256];
# endif # endif
#endif #endif
#ifdef NATIVE_BIG_ENDIAN
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
#include <sys/endian.h>
#define bswap_64 bswap64
#define bswap_32 bswap32
#define bswap_16 bswap16
#else
#include <byteswap.h>
#endif
#endif
#endif /* __sys_include__ */ #endif /* __sys_include__ */

View File

@ -26,6 +26,7 @@
#else #else
#include <sys/resource.h> #include <sys/resource.h>
#include <utime.h> #include <utime.h>
#include <dirent.h>
#endif #endif
#include <fcntl.h> #include <fcntl.h>
#include <signal.h> #include <signal.h>
@ -35,3 +36,4 @@
#ifdef USE_LIBCURL #ifdef USE_LIBCURL
#include <curl/curl.h> #include <curl/curl.h>
#endif #endif
#include <argon2.h>

View File

@ -54,7 +54,7 @@
* Can be useful if the above 3 versionids are insufficient for you (eg: you want to support CVS). * Can be useful if the above 3 versionids are insufficient for you (eg: you want to support CVS).
* This is updated automatically on the CVS server every Monday. so don't touch it. * This is updated automatically on the CVS server every Monday. so don't touch it.
*/ */
#define UNREAL_VERSION_TIME 202001 #define UNREAL_VERSION_TIME 202120
#define UnrealProtocol 5002 #define UnrealProtocol 5002
#define PATCH1 macro_to_str(UNREAL_VERSION_GENERATION) #define PATCH1 macro_to_str(UNREAL_VERSION_GENERATION)

View File

@ -60,13 +60,13 @@
#define UNREAL_VERSION_GENERATION 5 #define UNREAL_VERSION_GENERATION 5
/* Major version number (e.g.: 2 for Unreal3.2*) */ /* Major version number (e.g.: 2 for Unreal3.2*) */
#define UNREAL_VERSION_MAJOR 0 #define UNREAL_VERSION_MAJOR 2
/* Minor version number (e.g.: 1 for Unreal3.2.1) */ /* Minor version number (e.g.: 1 for Unreal3.2.1) */
#define UNREAL_VERSION_MINOR 9 #define UNREAL_VERSION_MINOR 0
/* Version suffix such as a beta marker or release candidate marker. (e.g.: /* Version suffix such as a beta marker or release candidate marker. (e.g.:
-rcX for unrealircd-3.2.9-rcX) */ -rcX for unrealircd-3.2.9-rcX) */
#define UNREAL_VERSION_SUFFIX "" #define UNREAL_VERSION_SUFFIX ".1"
#endif #endif

View File

@ -31,7 +31,7 @@ OBJS=dns.o auth.o channel.o crule.o dbuf.o \
api-moddata.o api-extban.o api-isupport.o api-command.o \ api-moddata.o api-extban.o api-isupport.o api-command.o \
api-clicap.o api-messagetag.o api-history-backend.o api-efunctions.o \ api-clicap.o api-messagetag.o api-history-backend.o api-efunctions.o \
api-event.o \ api-event.o \
crypt_blowfish.o updconf.o crashreport.o modulemanager.o \ crypt_blowfish.o unrealdb.o updconf.o crashreport.o modulemanager.o \
utf8.o \ utf8.o \
openssl_hostname_validation.o $(URL) openssl_hostname_validation.o $(URL)
@ -224,6 +224,9 @@ api-efunctions.o: api-efunctions.c $(INCLUDES)
crypt_blowfish.o: crypt_blowfish.c $(INCLUDES) crypt_blowfish.o: crypt_blowfish.c $(INCLUDES)
$(CC) $(CFLAGS) $(BINCFLAGS) -c crypt_blowfish.c $(CC) $(CFLAGS) $(BINCFLAGS) -c crypt_blowfish.c
unrealdb.o: unrealdb.c $(INCLUDES)
$(CC) $(CFLAGS) $(BINCFLAGS) -c unrealdb.c
updconf.o: updconf.c $(INCLUDES) updconf.o: updconf.c $(INCLUDES)
$(CC) $(CFLAGS) $(BINCFLAGS) -c updconf.c $(CC) $(CFLAGS) $(BINCFLAGS) -c updconf.c

View File

@ -66,7 +66,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char *
{ {
if (SERVICES_NAME && (acptr = find_person(alias->nick, NULL))) if (SERVICES_NAME && (acptr = find_person(alias->nick, NULL)))
{ {
if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_USERMSG, alias->nick, 0, NULL)) if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_USERMSG, cmd, alias->nick, 0, NULL))
return; return;
sendto_one(acptr, NULL, ":%s PRIVMSG %s@%s :%s", client->name, sendto_one(acptr, NULL, ":%s PRIVMSG %s@%s :%s", client->name,
alias->nick, SERVICES_NAME, parv[1]); alias->nick, SERVICES_NAME, parv[1]);
@ -78,7 +78,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char *
{ {
if (STATS_SERVER && (acptr = find_person(alias->nick, NULL))) if (STATS_SERVER && (acptr = find_person(alias->nick, NULL)))
{ {
if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_USERMSG, alias->nick, 0, NULL)) if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_USERMSG, cmd, alias->nick, 0, NULL))
return; return;
sendto_one(acptr, NULL, ":%s PRIVMSG %s@%s :%s", client->name, sendto_one(acptr, NULL, ":%s PRIVMSG %s@%s :%s", client->name,
alias->nick, STATS_SERVER, parv[1]); alias->nick, STATS_SERVER, parv[1]);
@ -90,7 +90,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char *
{ {
if ((acptr = find_person(alias->nick, NULL))) if ((acptr = find_person(alias->nick, NULL)))
{ {
if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_USERMSG, alias->nick, 0, NULL)) if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_USERMSG, cmd, alias->nick, 0, NULL))
return; return;
if (MyUser(acptr)) if (MyUser(acptr))
sendto_one(acptr, NULL, ":%s!%s@%s PRIVMSG %s :%s", client->name, sendto_one(acptr, NULL, ":%s!%s@%s PRIVMSG %s :%s", client->name,
@ -112,7 +112,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char *
char *errmsg = NULL; char *errmsg = NULL;
if (can_send_to_channel(client, channel, &msg, &errmsg, 0)) if (can_send_to_channel(client, channel, &msg, &errmsg, 0))
{ {
if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_CHANMSG, channel->chname, 0, NULL)) if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_CHANMSG, cmd, channel->chname, 0, NULL))
return; return;
new_message(client, NULL, &mtags); new_message(client, NULL, &mtags);
sendto_channel(channel, client, client->direction, sendto_channel(channel, client, client->direction,
@ -203,7 +203,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char *
{ {
if (SERVICES_NAME && (acptr = find_person(format->nick, NULL))) if (SERVICES_NAME && (acptr = find_person(format->nick, NULL)))
{ {
if (alias->spamfilter && match_spamfilter(client, output, SPAMF_USERMSG, format->nick, 0, NULL)) if (alias->spamfilter && match_spamfilter(client, output, SPAMF_USERMSG, cmd, format->nick, 0, NULL))
return; return;
sendto_one(acptr, NULL, ":%s PRIVMSG %s@%s :%s", client->name, sendto_one(acptr, NULL, ":%s PRIVMSG %s@%s :%s", client->name,
format->nick, SERVICES_NAME, output); format->nick, SERVICES_NAME, output);
@ -214,7 +214,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char *
{ {
if (STATS_SERVER && (acptr = find_person(format->nick, NULL))) if (STATS_SERVER && (acptr = find_person(format->nick, NULL)))
{ {
if (alias->spamfilter && match_spamfilter(client, output, SPAMF_USERMSG, format->nick, 0, NULL)) if (alias->spamfilter && match_spamfilter(client, output, SPAMF_USERMSG, cmd, format->nick, 0, NULL))
return; return;
sendto_one(acptr, NULL, ":%s PRIVMSG %s@%s :%s", client->name, sendto_one(acptr, NULL, ":%s PRIVMSG %s@%s :%s", client->name,
format->nick, STATS_SERVER, output); format->nick, STATS_SERVER, output);
@ -225,7 +225,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char *
{ {
if ((acptr = find_person(format->nick, NULL))) if ((acptr = find_person(format->nick, NULL)))
{ {
if (alias->spamfilter && match_spamfilter(client, output, SPAMF_USERMSG, format->nick, 0, NULL)) if (alias->spamfilter && match_spamfilter(client, output, SPAMF_USERMSG, cmd, format->nick, 0, NULL))
return; return;
if (MyUser(acptr)) if (MyUser(acptr))
sendto_one(acptr, NULL, ":%s!%s@%s PRIVMSG %s :%s", client->name, sendto_one(acptr, NULL, ":%s!%s@%s PRIVMSG %s :%s", client->name,
@ -247,7 +247,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char *
char *errmsg = NULL; char *errmsg = NULL;
if (!can_send_to_channel(client, channel, &msg, &errmsg, 0)) if (!can_send_to_channel(client, channel, &msg, &errmsg, 0))
{ {
if (alias->spamfilter && match_spamfilter(client, output, SPAMF_CHANMSG, channel->chname, 0, NULL)) if (alias->spamfilter && match_spamfilter(client, output, SPAMF_CHANMSG, cmd, channel->chname, 0, NULL))
return; return;
new_message(client, NULL, &mtags); new_message(client, NULL, &mtags);
sendto_channel(channel, client, client->direction, sendto_channel(channel, client, client->direction,

View File

@ -67,7 +67,8 @@ void (*tkl_stats)(Client *client, int type, char *para, int *cnt);
void (*tkl_sync)(Client *client); void (*tkl_sync)(Client *client);
void (*cmd_tkl)(Client *client, MessageTag *mtags, int parc, char *parv[]); void (*cmd_tkl)(Client *client, MessageTag *mtags, int parc, char *parv[]);
int (*place_host_ban)(Client *client, BanAction action, char *reason, long duration); int (*place_host_ban)(Client *client, BanAction action, char *reason, long duration);
int (*match_spamfilter)(Client *client, char *str_in, int type, char *target, int flags, TKL **rettk); int (*match_spamfilter)(Client *client, char *str_in, int type, char *cmd, char *target, int flags, TKL **rettk);
int (*match_spamfilter_mtags)(Client *client, MessageTag *mtags, char *cmd);
int (*join_viruschan)(Client *client, TKL *tk, int type); int (*join_viruschan)(Client *client, TKL *tk, int type);
unsigned char *(*StripColors)(unsigned char *text); unsigned char *(*StripColors)(unsigned char *text);
const char *(*StripControlCodes)(unsigned char *text); const char *(*StripControlCodes)(unsigned char *text);
@ -306,8 +307,9 @@ void efunctions_init(void)
efunc_init_function(EFUNC_TKL_SYNCH, tkl_sync, NULL); efunc_init_function(EFUNC_TKL_SYNCH, tkl_sync, NULL);
efunc_init_function(EFUNC_CMD_TKL, cmd_tkl, NULL); efunc_init_function(EFUNC_CMD_TKL, cmd_tkl, NULL);
efunc_init_function(EFUNC_PLACE_HOST_BAN, place_host_ban, NULL); efunc_init_function(EFUNC_PLACE_HOST_BAN, place_host_ban, NULL);
efunc_init_function(EFUNC_DOSPAMFILTER, match_spamfilter, NULL); efunc_init_function(EFUNC_MATCH_SPAMFILTER, match_spamfilter, NULL);
efunc_init_function(EFUNC_DOSPAMFILTER_VIRUSCHAN, join_viruschan, NULL); efunc_init_function(EFUNC_MATCH_SPAMFILTER_MTAGS, match_spamfilter_mtags, NULL);
efunc_init_function(EFUNC_JOIN_VIRUSCHAN, join_viruschan, NULL);
efunc_init_function(EFUNC_STRIPCOLORS, StripColors, NULL); efunc_init_function(EFUNC_STRIPCOLORS, StripColors, NULL);
efunc_init_function(EFUNC_STRIPCONTROLCODES, StripControlCodes, NULL); efunc_init_function(EFUNC_STRIPCONTROLCODES, StripControlCodes, NULL);
efunc_init_function(EFUNC_SPAMFILTER_BUILD_USER_STRING, spamfilter_build_user_string, NULL); efunc_init_function(EFUNC_SPAMFILTER_BUILD_USER_STRING, spamfilter_build_user_string, NULL);

View File

@ -28,6 +28,7 @@ ID_Copyright("(C) Carsten Munk 2001");
MODVAR Event *events = NULL; MODVAR Event *events = NULL;
extern EVENT(unrealdns_removeoldrecords); extern EVENT(unrealdns_removeoldrecords);
extern EVENT(unrealdb_expire_secret_cache);
/** Add an event, a function that will run at regular intervals. /** Add an event, a function that will run at regular intervals.
* @param module Module that this event belongs to * @param module Module that this event belongs to
@ -241,4 +242,5 @@ void SetupEvents(void)
EventAdd(NULL, "handshake_timeout", handshake_timeout, NULL, 1000, 0); EventAdd(NULL, "handshake_timeout", handshake_timeout, NULL, 1000, 0);
EventAdd(NULL, "try_connections", try_connections, NULL, 2000, 0); EventAdd(NULL, "try_connections", try_connections, NULL, 2000, 0);
EventAdd(NULL, "tls_check_expiry", tls_check_expiry, NULL, (86400/2)*1000, 0); EventAdd(NULL, "tls_check_expiry", tls_check_expiry, NULL, (86400/2)*1000, 0);
EventAdd(NULL, "unrealdb_expire_secret_cache", unrealdb_expire_secret_cache, NULL, 61000, 0);
} }

View File

@ -166,14 +166,21 @@ int history_add(char *object, MessageTag *mtags, char *line)
return 1; return 1;
} }
int history_request(Client *client, char *object, HistoryFilter *filter) HistoryResult *history_request(char *object, HistoryFilter *filter)
{ {
HistoryBackend *hb; HistoryBackend *hb = historybackends;
HistoryResult *r;
HistoryLogLine *l;
for (hb = historybackends; hb; hb=hb->next) if (!hb)
hb->history_request(client, object, filter); return 0; /* no history backend loaded */
return 1; /* Right now we return whenever the first backend has a result. */
for (hb = historybackends; hb; hb = hb->next)
if ((r = hb->history_request(object, filter)))
return r;
return NULL;
} }
int history_destroy(char *object) int history_destroy(char *object)
@ -195,3 +202,83 @@ int history_set_limit(char *object, int max_lines, long max_t)
return 1; return 1;
} }
/** Free a HistoryResult object that was returned from request_result() earlier */
void free_history_result(HistoryResult *r)
{
HistoryLogLine *l, *l_next;
for (l = r->log; l; l = l_next)
{
l_next = l->next;
free_message_tags(l->mtags);
safe_free(l);
}
safe_free(r->object);
safe_free(r);
}
/** Returns 1 if the client can receive channel history, 0 if not.
* @param client The client to check.
* @note It is recommend to call this function BEFORE trying to
* retrieve channel history via history_request(),
* as to not waste useless resources.
*/
int can_receive_history(Client *client)
{
if (HasCapability(client, "server-time"))
return 1;
return 0;
}
static void history_send_result_line(Client *client, HistoryLogLine *l, char *batchid)
{
if (BadPtr(batchid))
{
sendto_one(client, l->mtags, "%s", l->line);
} else {
MessageTag *m = safe_alloc(sizeof(MessageTag));
m->name = "batch";
m->value = batchid;
AddListItem(m, l->mtags);
sendto_one(client, l->mtags, "%s", l->line);
DelListItem(m, l->mtags);
safe_free(m);
}
}
/** Send the result of a history_request() to the client.
* @param client The client to send to.
* @param r The history result retrieved via history_request().
*/
void history_send_result(Client *client, HistoryResult *r)
{
char batch[BATCHLEN+1];
HistoryLogLine *l;
if (!can_receive_history(client))
return;
batch[0] = '\0';
if (HasCapability(client, "batch"))
{
/* Start a new batch */
generate_batch_id(batch);
sendto_one(client, NULL, ":%s BATCH +%s chathistory %s", me.name, batch, r->object);
}
for (l = r->log; l; l = l->next)
history_send_result_line(client, l, batch);
/* End of batch */
if (*batch)
sendto_one(client, NULL, ":%s BATCH -%s", me.name, batch);
}
void free_history_filter(HistoryFilter *f)
{
safe_free(f->timestamp_a);
safe_free(f->msgid_a);
safe_free(f->timestamp_b);
safe_free(f->msgid_b);
safe_free(f);
}

View File

@ -20,7 +20,6 @@
#include "unrealircd.h" #include "unrealircd.h"
#include "crypt_blowfish.h" #include "crypt_blowfish.h"
#include <argon2.h>
typedef struct AuthTypeList AuthTypeList; typedef struct AuthTypeList AuthTypeList;
struct AuthTypeList { struct AuthTypeList {

1154
src/conf.c

File diff suppressed because it is too large Load Diff

View File

@ -12,16 +12,6 @@ extern ConfigFile *conf;
NameValueList *config_defines = NULL; /**< List of @defines, only valid during configuration reading */ NameValueList *config_defines = NULL; /**< List of @defines, only valid during configuration reading */
void skip_whitespace(char **p)
{
for (; **p == ' ' || **p == '\t'; *p = *p + 1);
}
void read_until(char **p, char *stopchars)
{
for (; **p && !strchr(stopchars, **p); *p = *p + 1);
}
static inline int ValidVarCharacter(char x) static inline int ValidVarCharacter(char x)
{ {
if (isupper(x) || isdigit(x) || strchr("_", x)) if (isupper(x) || isdigit(x) || strchr("_", x))

View File

@ -4,9 +4,7 @@
*/ */
#include "unrealircd.h" #include "unrealircd.h"
#ifndef _WIN32 #ifdef _WIN32
#include <dirent.h>
#else
extern void StartUnrealAgain(void); extern void StartUnrealAgain(void);
#endif #endif
#include "version.h" #include "version.h"
@ -527,24 +525,6 @@ char *generate_crash_report(char *coredump, int *thirdpartymods)
return reportfname; return reportfname;
} }
int running_interactive(void)
{
#ifndef _WIN32
char *s;
if (!isatty(0))
return 0;
s = getenv("TERM");
if (!s || !strcasecmp(s, "dumb") || !strcasecmp(s, "none"))
return 0;
return 1;
#else
return IsService ? 0 : 1;
#endif
}
#define REPORT_NEVER -1 #define REPORT_NEVER -1
#define REPORT_ASK 0 #define REPORT_ASK 0
#define REPORT_AUTO 1 #define REPORT_AUTO 1
@ -728,13 +708,41 @@ void report_crash_not_sent(char *fname)
" (if you do, please set the option 'View Status' at the end of the bug report page to 'private'!!)\n", fname); " (if you do, please set the option 'View Status' at the end of the bug report page to 'private'!!)\n", fname);
} }
/** This checks if there are indications that 3rd party modules are
* loaded. This is used to provide a small warning to the user that
* the crash may be likely due to that.
*/
int check_third_party_mods_present(void)
{
#ifndef _WIN32
struct dirent *dir;
DIR *fd = opendir(TMPDIR);
if (!fd)
return 0;
/* We search for files like tmp/FC5C3116.third.somename.so */
while ((dir = readdir(fd)))
{
char *fname = dir->d_name;
if (strstr(fname, ".third.") && strstr(fname, ".so"))
{
closedir(fd);
return 1;
}
}
closedir(fd);
#endif
return 0;
}
void report_crash(void) void report_crash(void)
{ {
char *coredump, *fname; char *coredump, *fname;
int thirdpartymods = 0; int thirdpartymods = 0;
int crashed_secs_ago; int crashed_secs_ago;
if (!running_interactive() && (report_pref != REPORT_AUTO)) if (!running_interactively() && (report_pref != REPORT_AUTO))
exit(0); /* don't bother if we run through cron or something similar */ exit(0); /* don't bother if we run through cron or something similar */
coredump = find_best_coredump(); coredump = find_best_coredump();
@ -750,6 +758,8 @@ void report_crash(void)
if (!fname) if (!fname)
return; return;
if (thirdpartymods == 0)
thirdpartymods = check_third_party_mods_present();
#ifndef _WIN32 #ifndef _WIN32
printf("The IRCd has been started now (and is running), but it did crash %d seconds ago.\n", crashed_secs_ago); printf("The IRCd has been started now (and is running), but it did crash %d seconds ago.\n", crashed_secs_ago);
printf("Crash report generated in: %s\n\n", fname); printf("Crash report generated in: %s\n\n", fname);
@ -761,10 +771,10 @@ void report_crash(void)
"by someone other than the UnrealIRCd team). If you installed new 3rd party\n" "by someone other than the UnrealIRCd team). If you installed new 3rd party\n"
"module(s) in the past few weeks we suggest to unload these modules and see if\n" "module(s) in the past few weeks we suggest to unload these modules and see if\n"
"the crash issue dissapears. If so, that module is probably to blame.\n" "the crash issue dissapears. If so, that module is probably to blame.\n"
"If you keep crashing without 3rd party modules then please do report it to\n" "If you keep crashing without any 3rd party modules loaded then please do report\n"
"the UnrealIRCd team.\n" "it to the UnrealIRCd team.\n"
"The reason we ask you to do this is because more than 95%% of the crash issues\n" "The reason we ask you to do this is because MORE THAN 95%% OF ALL CRASH ISSUES\n"
"reported nowadays are caused by 3rd party modules and not by an UnrealIRCd bug.\n" "ARE CAUSED BY 3RD PARTY MODULES and not by an UnrealIRCd bug.\n"
"\n"); "\n");
} }
@ -778,7 +788,9 @@ void report_crash(void)
char answerbuf[64], *answer; char answerbuf[64], *answer;
printf("Shall I send a crash report to the UnrealIRCd developers?\n"); printf("Shall I send a crash report to the UnrealIRCd developers?\n");
if (!thirdpartymods) if (!thirdpartymods)
printf("Crash reports help us greatly with fixing bugs that affect you and others\n"); printf("Crash reports help us greatly with fixing bugs that affect you and others\n");
else
printf("NOTE: If the crash is caused by a 3rd party module then UnrealIRCd devs can't fix that.\n");
printf("\n"); printf("\n");
do do
@ -805,7 +817,7 @@ void report_crash(void)
return; return;
} }
if (running_interactive()) if (running_interactively())
{ {
char buf[8192], *line; char buf[8192], *line;

View File

@ -465,7 +465,7 @@ Client *hash_find_nickatserver(const char *str, Client *def)
if (serv) if (serv)
*serv++ = '\0'; *serv++ = '\0';
client = find_client(nick, NULL); client = find_person(nick, NULL);
if (!client) if (!client)
return NULL; /* client not found */ return NULL; /* client not found */

View File

@ -19,6 +19,7 @@
*/ */
#include "unrealircd.h" #include "unrealircd.h"
#include <ares.h>
#ifdef __FreeBSD__ #ifdef __FreeBSD__
char *malloc_options = "h" MALLOC_FLAGS_EXTRA; char *malloc_options = "h" MALLOC_FLAGS_EXTRA;
@ -60,6 +61,7 @@ static void open_debugfile(), setup_signals();
extern void init_glines(void); extern void init_glines(void);
extern void tkl_init(void); extern void tkl_init(void);
extern void process_clients(void); extern void process_clients(void);
extern void unrealdb_test(void);
#ifndef _WIN32 #ifndef _WIN32
MODVAR char **myargv; MODVAR char **myargv;
@ -80,6 +82,7 @@ void s_die()
Client *client; Client *client;
if (!IsService) if (!IsService)
{ {
loop.ircd_terminating = 1;
unload_all_modules(); unload_all_modules();
list_for_each_entry(client, &lclient_list, lclient_node) list_for_each_entry(client, &lclient_list, lclient_node)
@ -94,6 +97,7 @@ void s_die()
ControlService(hService, SERVICE_CONTROL_STOP, &status); ControlService(hService, SERVICE_CONTROL_STOP, &status);
} }
#else #else
loop.ircd_terminating = 1;
unload_all_modules(); unload_all_modules();
unlink(conf_files ? conf_files->pid_file : IRCD_PIDFILE); unlink(conf_files ? conf_files->pid_file : IRCD_PIDFILE);
exit(0); exit(0);
@ -366,7 +370,7 @@ int match_tkls(Client *client)
if (loop.do_bancheck_spamf_away && IsUser(client) && if (loop.do_bancheck_spamf_away && IsUser(client) &&
client->user->away != NULL && client->user->away != NULL &&
match_spamfilter(client, client->user->away, SPAMF_AWAY, NULL, SPAMFLAG_NOWARN, NULL)) match_spamfilter(client, client->user->away, SPAMF_AWAY, "AWAY", NULL, SPAMFLAG_NOWARN, NULL))
{ {
return 1; return 1;
} }
@ -573,84 +577,6 @@ char buf[1024];
#endif #endif
} }
/** Ugly version checker that ensures ssl/curl runtime libraries match the
* version we compiled for.
*/
static void do_version_check()
{
const char *compiledfor, *runtime;
int error = 0;
char *p;
/* OPENSSL:
* Nowadays (since openssl 1.0.0) they retain binary compatibility
* when the first two version numbers are the same: eg 1.0.0 and 1.0.2
*/
compiledfor = OPENSSL_VERSION_TEXT;
runtime = SSLeay_version(SSLEAY_VERSION);
p = strchr(compiledfor, '.');
if (p)
{
p = strchr(p+1, '.');
if (p)
{
int versionlen = p - compiledfor + 1;
if (strncasecmp(compiledfor, runtime, versionlen))
{
version_check_logerror("OpenSSL version mismatch: compiled for '%s', library is '%s'",
compiledfor, runtime);
error=1;
}
}
}
#ifdef USE_LIBCURL
/* Perhaps someone should tell them to do this a bit more easy ;)
* problem is runtime output is like: 'libcurl/7.11.1 c-ares/1.2.0'
* while header output is like: '7.11.1'.
*/
{
char buf[128], *p;
runtime = curl_version();
compiledfor = LIBCURL_VERSION;
if (!strncmp(runtime, "libcurl/", 8))
{
strlcpy(buf, runtime+8, sizeof(buf));
p = strchr(buf, ' ');
if (p)
{
*p = '\0';
if (strcmp(compiledfor, buf))
{
version_check_logerror("Curl version mismatch: compiled for '%s', library is '%s'",
compiledfor, buf);
error = 1;
}
}
}
}
#endif
if (error)
{
#ifndef _WIN32
version_check_logerror("Header<->library mismatches can make UnrealIRCd *CRASH*! "
"Make sure you don't have multiple versions of openssl installed (eg: "
"one in /usr and one in /usr/local). And, if you recently upgraded them, "
"be sure to recompile UnrealIRCd.");
#else
version_check_logerror("Header<->library mismatches can make UnrealIRCd *CRASH*! "
"This should never happen with official Windows builds... unless "
"you overwrote any .dll files with newer/older ones or something.");
win_error();
#endif
tainted = 1;
}
}
extern void applymeblock(void); extern void applymeblock(void);
extern MODVAR Event *events; extern MODVAR Event *events;
@ -927,6 +853,11 @@ int InitUnrealIRCd(int argc, char *argv[])
safe_strdup(configfile, CONFIGFILE); safe_strdup(configfile, CONFIGFILE);
init_random(); /* needs to be done very early!! */ init_random(); /* needs to be done very early!! */
if (sodium_init() < 0)
{
fprintf(stderr, "Failed to initialize sodium library -- error accessing random device?\n");
exit(-1);
}
memset(&botmotd, '\0', sizeof(MOTDFile)); memset(&botmotd, '\0', sizeof(MOTDFile));
memset(&rules, '\0', sizeof(MOTDFile)); memset(&rules, '\0', sizeof(MOTDFile));
@ -1070,9 +1001,10 @@ int InitUnrealIRCd(int argc, char *argv[])
exit(0); exit(0);
} }
#endif #endif
#if 0 #if 1
case 'S': case 'S':
charsys_dump_table(p ? p : "*"); //charsys_dump_table(p ? p : "*");
unrealdb_test();
exit(0); exit(0);
#endif #endif
#ifndef _WIN32 #ifndef _WIN32
@ -1165,8 +1097,6 @@ int InitUnrealIRCd(int argc, char *argv[])
} }
} }
do_version_check();
#if !defined(_WIN32) #if !defined(_WIN32)
#ifndef _WIN32 #ifndef _WIN32
mkdir(TMPDIR, S_IRUSR|S_IWUSR|S_IXUSR); /* Create the tmp dir, if it doesn't exist */ mkdir(TMPDIR, S_IRUSR|S_IWUSR|S_IXUSR); /* Create the tmp dir, if it doesn't exist */
@ -1215,11 +1145,13 @@ int InitUnrealIRCd(int argc, char *argv[])
fprintf(stderr, "UnrealIRCd is brought to you by Bram Matthys (Syzop), Gottem and i\n\n"); fprintf(stderr, "UnrealIRCd is brought to you by Bram Matthys (Syzop), Gottem and i\n\n");
fprintf(stderr, "Using the following libraries:\n"); fprintf(stderr, "Using the following libraries:\n");
fprintf(stderr, "* %s\n", pcre2_version());
fprintf(stderr, "* %s\n", SSLeay_version(SSLEAY_VERSION)); fprintf(stderr, "* %s\n", SSLeay_version(SSLEAY_VERSION));
fprintf(stderr, "* libsodium %s\n", sodium_version_string());
#ifdef USE_LIBCURL #ifdef USE_LIBCURL
fprintf(stderr, "* %s\n", curl_version()); fprintf(stderr, "* %s\n", curl_version());
#endif #endif
fprintf(stderr, "* c-ares %s\n", ares_version(NULL));
fprintf(stderr, "* %s\n", pcre2_version());
#endif #endif
check_user_limit(); check_user_limit();
#ifndef _WIN32 #ifndef _WIN32

View File

@ -76,7 +76,7 @@ void initlists(void)
client_pool = mp_pool_new(sizeof(Client), 512 * 1024); client_pool = mp_pool_new(sizeof(Client), 512 * 1024);
local_client_pool = mp_pool_new(sizeof(LocalClient), 512 * 1024); local_client_pool = mp_pool_new(sizeof(LocalClient), 512 * 1024);
user_pool = mp_pool_new(sizeof(ClientUser), 512 * 1024); user_pool = mp_pool_new(sizeof(User), 512 * 1024);
link_pool = mp_pool_new(sizeof(Link), 512 * 1024); link_pool = mp_pool_new(sizeof(Link), 512 * 1024);
} }
@ -184,30 +184,20 @@ void free_client(Client *client)
** 'make_user' add's an User information block to a client ** 'make_user' add's an User information block to a client
** if it was not previously allocated. ** if it was not previously allocated.
*/ */
ClientUser *make_user(Client *client) User *make_user(Client *client)
{ {
ClientUser *user; User *user;
user = client->user; user = client->user;
if (!user) if (!user)
{ {
user = mp_pool_get(user_pool); user = mp_pool_get(user_pool);
memset(user, 0, sizeof(ClientUser)); memset(user, 0, sizeof(User));
#ifdef DEBUGMODE #ifdef DEBUGMODE
users.inuse++; users.inuse++;
#endif #endif
user->swhois = NULL;
user->away = NULL;
user->flood.away_t = 0;
user->flood.away_c = 0;
user->joined = 0;
user->channel = NULL;
user->invited = NULL;
user->server = NULL;
strlcpy(user->svid, "0", sizeof(user->svid)); strlcpy(user->svid, "0", sizeof(user->svid));
user->whowas = NULL;
user->snomask = 0;
if (client->ip) if (client->ip)
{ {
/* initially set client->user->realhost to IP */ /* initially set client->user->realhost to IP */
@ -450,6 +440,8 @@ void del_ListItem(ListStruct *item, ListStruct **list)
item->next->prev = item->prev; item->next->prev = item->prev;
if (*list == item) if (*list == item)
*list = item->next; /* new head */ *list = item->next; /* new head */
/* And update 'item', prev/next should point nowhere anymore */
item->prev = item->next = NULL;
} }
/** Add item to list with a 'priority'. /** Add item to list with a 'priority'.

View File

@ -506,11 +506,6 @@ mp_pool_new(size_t item_size, size_t chunk_capacity)
pool->next = mp_allocated_pools; pool->next = mp_allocated_pools;
mp_allocated_pools = pool; mp_allocated_pools = pool;
ircd_log(LOG_DBG, "Capacity is %lu, item size is %lu, alloc size is %lu",
(unsigned long)pool->new_chunk_capacity,
(unsigned long)pool->item_alloc_size,
(unsigned long)(pool->new_chunk_capacity*pool->item_alloc_size));
return pool; return pool;
} }
@ -701,19 +696,12 @@ mp_pool_log_status(mp_pool_t *pool)
for (chunk = pool->empty_chunks; chunk; chunk = chunk->next) for (chunk = pool->empty_chunks; chunk; chunk = chunk->next)
bytes_allocated += chunk->mem_size; bytes_allocated += chunk->mem_size;
ircd_log(LOG_DBG, "%llu bytes in %d empty chunks",
bytes_allocated, pool->n_empty_chunks);
for (chunk = pool->used_chunks; chunk; chunk = chunk->next) { for (chunk = pool->used_chunks; chunk; chunk = chunk->next) {
++n_used; ++n_used;
bu += chunk->n_allocated * pool->item_alloc_size; bu += chunk->n_allocated * pool->item_alloc_size;
ba += chunk->mem_size; ba += chunk->mem_size;
ircd_log(LOG_DBG, " used chunk: %d items allocated",
chunk->n_allocated);
} }
ircd_log(LOG_DBG, "%llu/%llu bytes in %d partially full chunks",
bu, ba, n_used);
bytes_used += bu; bytes_used += bu;
bytes_allocated += ba; bytes_allocated += ba;
bu = ba = 0; bu = ba = 0;
@ -724,22 +712,7 @@ mp_pool_log_status(mp_pool_t *pool)
ba += chunk->mem_size; ba += chunk->mem_size;
} }
ircd_log(LOG_DBG, "%llu/%llu bytes in %d full chunks",
bu, ba, n_full);
bytes_used += bu; bytes_used += bu;
bytes_allocated += ba; bytes_allocated += ba;
ircd_log(LOG_DBG, "Total: %llu/%llu bytes allocated "
"for cell pools are full.",
bytes_used, bytes_allocated);
#ifdef MEMPOOL_STATS
ircd_log(LOG_DBG, "%llu cell allocations ever; "
"%llu chunk allocations ever; "
"%llu chunk frees ever.",
(long long)pool->total_items_allocated,
(long long)pool->total_chunks_allocated,
(long long)pool->total_chunks_freed);
#endif
} }
#endif #endif

View File

@ -81,16 +81,17 @@ typedef struct {
} SpamfilterTargetTable; } SpamfilterTargetTable;
SpamfilterTargetTable spamfiltertargettable[] = { SpamfilterTargetTable spamfiltertargettable[] = {
{ SPAMF_CHANMSG, 'c', "channel", "PRIVMSG" }, { SPAMF_CHANMSG, 'c', "channel", "PRIVMSG" },
{ SPAMF_USERMSG, 'p', "private", "PRIVMSG" }, { SPAMF_USERMSG, 'p', "private", "PRIVMSG" },
{ SPAMF_USERNOTICE, 'n', "private-notice", "NOTICE" }, { SPAMF_USERNOTICE, 'n', "private-notice", "NOTICE" },
{ SPAMF_CHANNOTICE, 'N', "channel-notice", "NOTICE" }, { SPAMF_CHANNOTICE, 'N', "channel-notice", "NOTICE" },
{ SPAMF_PART, 'P', "part", "PART" }, { SPAMF_PART, 'P', "part", "PART" },
{ SPAMF_QUIT, 'q', "quit", "QUIT" }, { SPAMF_QUIT, 'q', "quit", "QUIT" },
{ SPAMF_DCC, 'd', "dcc", "PRIVMSG" }, { SPAMF_DCC, 'd', "dcc", "PRIVMSG" },
{ SPAMF_USER, 'u', "user", "NICK" }, { SPAMF_USER, 'u', "user", "NICK" },
{ SPAMF_AWAY, 'a', "away", "AWAY" }, { SPAMF_AWAY, 'a', "away", "AWAY" },
{ SPAMF_TOPIC, 't', "topic", "TOPIC" }, { SPAMF_TOPIC, 't', "topic", "TOPIC" },
{ SPAMF_MTAG, 'T', "message-tag", "message-tag" },
{ 0, 0, 0, 0 } { 0, 0, 0, 0 }
}; };
@ -583,7 +584,7 @@ static void recurse_send_quits(Client *cptr, Client *client, Client *from, Clien
recurse_send_quits(cptr, acptr, from, to, mtags, comment, splitstr); recurse_send_quits(cptr, acptr, from, to, mtags, comment, splitstr);
} }
if (cptr == client && to != from) if (cptr == client && to != from && !(to->direction && (to->direction == from)))
sendto_one(to, mtags, "SQUIT %s :%s", client->name, comment); sendto_one(to, mtags, "SQUIT %s :%s", client->name, comment);
} }
@ -699,6 +700,16 @@ static void exit_one_client(Client *client, MessageTag *mtags_i, const char *com
* @param comment The (s)quit message * @param comment The (s)quit message
*/ */
void exit_client(Client *client, MessageTag *recv_mtags, char *comment) void exit_client(Client *client, MessageTag *recv_mtags, char *comment)
{
exit_client_ex(client, client->direction, recv_mtags, comment);
}
/** Exit this IRC client, and all the dependents (users, servers) if this is a server.
* @param client The client to exit.
* @param recv_mtags Message tags to use as a base (if any).
* @param comment The (s)quit message
*/
void exit_client_ex(Client *client, Client *origin, MessageTag *recv_mtags, char *comment)
{ {
long long on_for; long long on_for;
ConfigItem_listen *listen_conf; ConfigItem_listen *listen_conf;
@ -812,7 +823,7 @@ void exit_client(Client *client, MessageTag *recv_mtags, char *comment)
else else
ircsnprintf(splitstr, sizeof splitstr, "%s %s", client->srvptr->name, client->name); ircsnprintf(splitstr, sizeof splitstr, "%s %s", client->srvptr->name, client->name);
remove_dependents(client, client->direction, recv_mtags, comment, splitstr); remove_dependents(client, origin, recv_mtags, comment, splitstr);
RunHook2(HOOKTYPE_SERVER_QUIT, client, recv_mtags); RunHook2(HOOKTYPE_SERVER_QUIT, client, recv_mtags);
} }
@ -1187,6 +1198,7 @@ char *our_strcasestr(char *haystack, char *needle)
* @param tag A tag used internally and for server-to-server traffic, * @param tag A tag used internally and for server-to-server traffic,
* not visible to end-users. * not visible to end-users.
* @param priority Priority - for ordering multiple swhois entries * @param priority Priority - for ordering multiple swhois entries
* (lower number = further up in the swhoises list in WHOIS)
* @param swhois The actual special whois title (string) you want to add to the user * @param swhois The actual special whois title (string) you want to add to the user
* @param from Who added this entry * @param from Who added this entry
* @param skip Which server(-side) to skip broadcasting this entry to. * @param skip Which server(-side) to skip broadcasting this entry to.
@ -1785,6 +1797,23 @@ int read_str(FILE *fd, char **x)
return 1; return 1;
} }
/** Convert binary 'data' of size 'len' to a hexadecimal string 'str'.
* The caller is responsible to ensure that 'str' is sufficiently large.
*/
void binarytohex(void *data, size_t len, char *str)
{
const char hexchars[16] = "0123456789abcdef";
char *datastr = (char *)data;
int i, n = 0;
for (i=0; i<len; i++)
{
str[n++] = hexchars[(datastr[i] >> 4) & 0xF];
str[n++] = hexchars[datastr[i] & 0xF];
}
str[n] = '\0';
}
/** Generates an MD5 checksum. /** Generates an MD5 checksum.
* @param mdout[out] Buffer to store result in, the result will be 16 bytes in binary * @param mdout[out] Buffer to store result in, the result will be 16 bytes in binary
* (not ascii printable!). * (not ascii printable!).
@ -1809,31 +1838,27 @@ void DoMD5(char *mdout, const char *src, unsigned long n)
char *md5hash(char *dst, const char *src, unsigned long n) char *md5hash(char *dst, const char *src, unsigned long n)
{ {
char tmp[16]; char tmp[16];
SHA256_CTX hash;
DoMD5(tmp, src, n); DoMD5(tmp, src, n);
sprintf(dst, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", binarytohex(tmp, sizeof(tmp), dst);
tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8],
tmp[9], tmp[10], tmp[11], tmp[12], tmp[13], tmp[14], tmp[15]);
return dst; return dst;
} }
/** Convert binary 'data' of size 'len' to a hexadecimal string 'str'. /** Generates a SHA256 checksum - ASCII printable string (0011223344..etc..).
* The caller is responsible to ensure that 'str' is sufficiently large. * @param dst[out] Buffer to store result in, which needs to be 65 bytes minimum.
* @param src[in] The input data used to generate the checksum.
* @param n[in] Length of data.
*/ */
void binarytohex(void *data, size_t len, char *str) char *sha256hash(char *dst, const char *src, unsigned long n)
{ {
const char hexchars[16] = "0123456789abcdef"; SHA256_CTX hash;
char *datastr = (char *)data; char binaryhash[SHA256_DIGEST_LENGTH];
int i, n = 0;
for (i=0; i<len; i++) SHA256_Init(&hash);
{ SHA256_Update(&hash, src, n);
str[n++] = hexchars[(datastr[i] >> 4) & 0xF]; SHA256_Final(binaryhash, &hash);
str[n++] = hexchars[datastr[i] & 0xF]; binarytohex(binaryhash, sizeof(binaryhash), dst);
} return dst;
str[n] = '\0';
} }
/** Calculate the SHA256 checksum of a file */ /** Calculate the SHA256 checksum of a file */
@ -1973,3 +1998,101 @@ char *sendtype_to_cmd(SendType sendtype)
return "TAGMSG"; return "TAGMSG";
return NULL; return NULL;
} }
/** Check password strength.
* @param pass The password to check
* @param min_length The minimum length of the password
* @param strict Whether to require UPPER+lower+digits
* @returns 1 if good, 0 if not.
*/
int check_password_strength(char *pass, int min_length, int strict, char **err)
{
char has_lowercase=0, has_uppercase=0, has_digit=0;
char *p;
static char buf[256];
if (err)
*err = NULL;
if (strlen(pass) < min_length)
{
if (err)
{
snprintf(buf, sizeof(buf), "Password must be at least %d characters", min_length);
*err = buf;
}
return 0;
}
for (p=pass; *p; p++)
{
if (islower(*p))
has_lowercase = 1;
else if (isupper(*p))
has_uppercase = 1;
else if (isdigit(*p))
has_digit = 1;
}
if (strict)
{
if (!has_lowercase)
{
if (err)
*err = "Password must contain at least 1 lowercase character";
return 0;
} else
if (!has_uppercase)
{
if (err)
*err = "Password must contain at least 1 UPPERcase character";
return 0;
} else
if (!has_digit)
{
if (err)
*err = "Password must contain at least 1 digit (number)";
return 0;
}
}
return 1;
}
int valid_secret_password(char *pass, char **err)
{
return check_password_strength(pass, 10, 1, err);
}
int running_interactively(void)
{
#ifndef _WIN32
char *s;
if (!isatty(0))
return 0;
s = getenv("TERM");
if (!s || !strcasecmp(s, "dumb") || !strcasecmp(s, "none"))
return 0;
return 1;
#else
return IsService ? 0 : 1;
#endif
}
/** Skip whitespace (if any) */
void skip_whitespace(char **p)
{
for (; **p == ' ' || **p == '\t'; *p = *p + 1);
}
/** Keep reading '*p' until we hit any of the 'stopchars'.
* Actually behaves like strstr() but then hit the end
* of the string (\0) i guess?
*/
void read_until(char **p, char *stopchars)
{
for (; **p && !strchr(stopchars, **p); *p = *p + 1);
}

View File

@ -6,7 +6,6 @@
#include "unrealircd.h" #include "unrealircd.h"
#ifndef _WIN32 #ifndef _WIN32
#include <dirent.h>
#define MODULEMANAGER_CONNECT_TIMEOUT 7 #define MODULEMANAGER_CONNECT_TIMEOUT 7
#define MODULEMANAGER_READ_TIMEOUT 20 #define MODULEMANAGER_READ_TIMEOUT 20

View File

@ -30,9 +30,6 @@
#else #else
#include <dlfcn.h> #include <dlfcn.h>
#endif #endif
#ifndef _WIN32
#include <dirent.h>
#endif
#ifndef RTLD_NOW #ifndef RTLD_NOW
#define RTLD_NOW RTLD_LAZY #define RTLD_NOW RTLD_LAZY
#endif #endif
@ -444,7 +441,7 @@ char *Module_Create(char *path_)
} }
} }
mod->flags = MODFLAG_TESTING; mod->flags = MODFLAG_TESTING;
AddListItem(mod, Modules); AddListItemPrio(mod, Modules, 0);
return NULL; return NULL;
} }
else else
@ -1187,6 +1184,9 @@ void unload_all_modules(void)
int (*Mod_Unload)(); int (*Mod_Unload)();
for (m = Modules; m; m = m->next) for (m = Modules; m; m = m->next)
{ {
#ifdef DEBUGMODE
ircd_log(LOG_ERROR, "Unloading %s...", m->header->name);
#endif
irc_dlsym(m->dll, "Mod_Unload", Mod_Unload); irc_dlsym(m->dll, "Mod_Unload", Mod_Unload);
if (Mod_Unload) if (Mod_Unload)
(*Mod_Unload)(&m->modinfo); (*Mod_Unload)(&m->modinfo);
@ -1194,15 +1194,21 @@ void unload_all_modules(void)
} }
} }
unsigned int ModuleSetOptions(Module *module, unsigned int options, int action) void ModuleSetOptions(Module *module, unsigned int options, int action)
{ {
unsigned int oldopts = module->options; unsigned int oldopts = module->options;
if (action) if (options == MOD_OPT_UNLOAD_PRIORITY)
module->options |= options; {
else DelListItem(module, Modules);
module->options &= ~options; AddListItemPrio(module, Modules, action);
return oldopts; } else {
/* Simple bit flag(s) */
if (action)
module->options |= options;
else
module->options &= ~options;
}
} }
unsigned int ModuleGetOptions(Module *module) unsigned int ModuleGetOptions(Module *module)

View File

@ -71,8 +71,9 @@ R_MODULES= \
account-tag.so labeled-response.so link-security.so \ account-tag.so labeled-response.so link-security.so \
message-ids.so plaintext-policy.so server-time.so sts.so \ message-ids.so plaintext-policy.so server-time.so sts.so \
echo-message.so userip-tag.so userhost-tag.so \ echo-message.so userip-tag.so userhost-tag.so \
typing-indicator.so \ bot-tag.so \
ident_lookup.so history.so \ reply-tag.so typing-indicator.so \
ident_lookup.so history.so chathistory.so \
targetfloodprot.so clienttagdeny.so targetfloodprot.so clienttagdeny.so
MODULES=cloak.so $(R_MODULES) MODULES=cloak.so $(R_MODULES)
@ -612,6 +613,14 @@ userhost-tag.so: userhost-tag.c $(INCLUDES)
$(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \
-o userhost-tag.so userhost-tag.c -o userhost-tag.so userhost-tag.c
bot-tag.so: bot-tag.c $(INCLUDES)
$(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \
-o bot-tag.so bot-tag.c
reply-tag.so: reply-tag.c $(INCLUDES)
$(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \
-o reply-tag.so reply-tag.c
typing-indicator.so: typing-indicator.c $(INCLUDES) typing-indicator.so: typing-indicator.c $(INCLUDES)
$(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \
-o typing-indicator.so typing-indicator.c -o typing-indicator.so typing-indicator.c
@ -632,6 +641,10 @@ history.so: history.c $(INCLUDES)
$(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \
-o history.so history.c -o history.so history.c
chathistory.so: chathistory.c $(INCLUDES)
$(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \
-o chathistory.so chathistory.c
targetfloodprot.so: targetfloodprot.c $(INCLUDES) targetfloodprot.so: targetfloodprot.c $(INCLUDES)
$(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \
-o targetfloodprot.so targetfloodprot.c -o targetfloodprot.so targetfloodprot.c

View File

@ -858,20 +858,6 @@ static int internal_getscore(char *str)
return score; return score;
} }
void strtolower_safe(char *dst, char *src, int size)
{
if (!size)
return; /* size of 0 is unworkable */
size--; /* for \0 */
for (; *src && size; src++)
{
*dst++ = tolower(*src);
size--;
}
*dst = '\0';
}
/** Returns "spam score". /** Returns "spam score".
* @note a user is expected, do not call for anything else (eg: servers) * @note a user is expected, do not call for anything else (eg: servers)
*/ */

View File

@ -451,7 +451,7 @@ int authprompt_sasl_continuation(Client *client, char *buf)
sendto_one(agent, NULL, ":%s SASL %s %s C %s", sendto_one(agent, NULL, ":%s SASL %s %s C %s",
me.id, AGENT_SID(agent), client->id, SEUSER(client)->authmsg); me.id, AGENT_SID(agent), client->id, SEUSER(client)->authmsg);
} }
SEUSER(client)->authmsg = NULL; safe_free(SEUSER(client)->authmsg);
} }
return 1; /* inhibit displaying of message */ return 1; /* inhibit displaying of message */
} }

View File

@ -86,24 +86,16 @@ CMD_FUNC(cmd_away)
} }
/* Check spamfilters */ /* Check spamfilters */
if (MyUser(client) && match_spamfilter(client, new_reason, SPAMF_AWAY, NULL, 0, NULL)) if (MyUser(client) && match_spamfilter(client, new_reason, SPAMF_AWAY, "AWAY", NULL, 0, NULL))
return; return;
/* Check set::anti-flood::away-flood */ /* Check away-flood */
if (MyUser(client) && AWAY_PERIOD && !ValidatePermissionsForPath("immune:away-flood",client,NULL,NULL,NULL)) if (MyUser(client) &&
!ValidatePermissionsForPath("immune:away-flood",client,NULL,NULL,NULL) &&
flood_limit_exceeded(client, FLD_AWAY))
{ {
if ((client->user->flood.away_t + AWAY_PERIOD) <= timeofday) sendnumeric(client, ERR_TOOMANYAWAY);
{ return;
client->user->flood.away_c = 0;
client->user->flood.away_t = timeofday;
}
if (client->user->flood.away_c <= AWAY_COUNT)
client->user->flood.away_c++;
if (client->user->flood.away_c > AWAY_COUNT)
{
sendnumeric(client, ERR_TOOMANYAWAY);
return;
}
} }
/* Obey set::away-length */ /* Obey set::away-length */

90
src/modules/bot-tag.c Normal file
View File

@ -0,0 +1,90 @@
/*
* IRC - Internet Relay Chat, src/modules/bot-tag.c
* (C) 2021 Syzop & The UnrealIRCd Team
*
* See file AUTHORS in IRC package for additional names of
* the programmers.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* This implements the message tag that is mentioned in
* https://ircv3.net/specs/extensions/bot-mode
* The B mode and 005 is in the modules/usermodes/bot module.
*/
#include "unrealircd.h"
ModuleHeader MOD_HEADER
= {
"bot-tag",
"5.0",
"bot message tag",
"UnrealIRCd Team",
"unrealircd-5",
};
int bottag_mtag_is_ok(Client *client, char *name, char *value);
void mtag_add_bottag(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature);
MOD_INIT()
{
MessageTagHandlerInfo mtag;
MARK_AS_OFFICIAL_MODULE(modinfo);
memset(&mtag, 0, sizeof(mtag));
mtag.name = "draft/bot";
mtag.is_ok = bottag_mtag_is_ok;
mtag.flags = MTAG_HANDLER_FLAGS_NO_CAP_NEEDED;
MessageTagHandlerAdd(modinfo->handle, &mtag);
HookAddVoid(modinfo->handle, HOOKTYPE_NEW_MESSAGE, 0, mtag_add_bottag);
return MOD_SUCCESS;
}
MOD_LOAD()
{
return MOD_SUCCESS;
}
MOD_UNLOAD()
{
return MOD_SUCCESS;
}
/** This function verifies if the client sending the mtag is permitted to do so.
*/
int bottag_mtag_is_ok(Client *client, char *name, char *value)
{
if (IsServer(client) && (value == NULL))
return 1; /* OK */
return 0;
}
void mtag_add_bottag(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature)
{
MessageTag *m;
if (IsUser(client) && has_user_mode(client, 'B'))
{
MessageTag *m = safe_alloc(sizeof(MessageTag));
safe_strdup(m->name, "draft/bot");
m->value = NULL;
AddListItem(m, *mtag_list);
}
}

View File

@ -28,13 +28,13 @@ ModuleHeader MOD_HEADER
"unrealircd-5", "unrealircd-5",
}; };
#define FLD_CTCP 0 /* c */ #define CHFLD_CTCP 0 /* c */
#define FLD_JOIN 1 /* j */ #define CHFLD_JOIN 1 /* j */
#define FLD_KNOCK 2 /* k */ #define CHFLD_KNOCK 2 /* k */
#define FLD_MSG 3 /* m */ #define CHFLD_MSG 3 /* m */
#define FLD_NICK 4 /* n */ #define CHFLD_NICK 4 /* n */
#define FLD_TEXT 5 /* t */ #define CHFLD_TEXT 5 /* t */
#define FLD_REPEAT 6 /* r */ #define CHFLD_REPEAT 6 /* r */
#define NUMFLD 7 /* 7 flood types */ #define NUMFLD 7 /* 7 flood types */
@ -58,13 +58,13 @@ typedef struct FloodType {
* IMPORTANT: MUST be in alphabetic order!! * IMPORTANT: MUST be in alphabetic order!!
*/ */
FloodType floodtypes[] = { FloodType floodtypes[] = {
{ 'c', FLD_CTCP, "CTCPflood", 'C', "mM", 0, }, { 'c', CHFLD_CTCP, "CTCPflood", 'C', "mM", 0, },
{ 'j', FLD_JOIN, "joinflood", 'i', "R", 0, }, { 'j', CHFLD_JOIN, "joinflood", 'i', "R", 0, },
{ 'k', FLD_KNOCK, "knockflood", 'K', "", 0, }, { 'k', CHFLD_KNOCK, "knockflood", 'K', "", 0, },
{ 'm', FLD_MSG, "msg/noticeflood", 'm', "M", 0, }, { 'm', CHFLD_MSG, "msg/noticeflood", 'm', "M", 0, },
{ 'n', FLD_NICK, "nickflood", 'N', "", 0, }, { 'n', CHFLD_NICK, "nickflood", 'N', "", 0, },
{ 't', FLD_TEXT, "msg/noticeflood", '\0', "bd", 1, }, { 't', CHFLD_TEXT, "msg/noticeflood", '\0', "bd", 1, },
{ 'r', FLD_REPEAT, "repeating", '\0', "bd", 1, }, { 'r', CHFLD_REPEAT, "repeating", '\0', "bd", 1, },
}; };
#define MODEF_DEFAULT_UNSETTIME cfg.modef_default_unsettime #define MODEF_DEFAULT_UNSETTIME cfg.modef_default_unsettime
@ -132,7 +132,7 @@ uint64_t gen_floodprot_msghash(char *text);
int cmodef_is_ok(Client *client, Channel *channel, char mode, char *para, int type, int what); int cmodef_is_ok(Client *client, Channel *channel, char mode, char *para, int type, int what);
void *cmodef_put_param(void *r_in, char *param); void *cmodef_put_param(void *r_in, char *param);
char *cmodef_get_param(void *r_in); char *cmodef_get_param(void *r_in);
char *cmodef_conv_param(char *param_in, Client *client); char *cmodef_conv_param(char *param_in, Client *client, Channel *channel);
void cmodef_free_param(void *r); void cmodef_free_param(void *r);
void *cmodef_dup_struct(void *r_in); void *cmodef_dup_struct(void *r_in);
int cmodef_sjoin_check(Channel *channel, void *ourx, void *theirx); int cmodef_sjoin_check(Channel *channel, void *ourx, void *theirx);
@ -142,7 +142,7 @@ int cmodef_channel_destroy(Channel *channel, int *should_destroy);
int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype); int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype);
int floodprot_post_chanmsg(Client *client, Channel *channel, int sendflags, int prefix, char *target, MessageTag *mtags, char *text, SendType sendtype); int floodprot_post_chanmsg(Client *client, Channel *channel, int sendflags, int prefix, char *target, MessageTag *mtags, char *text, SendType sendtype);
int floodprot_knock(Client *client, Channel *channel, MessageTag *mtags, char *comment); int floodprot_knock(Client *client, Channel *channel, MessageTag *mtags, char *comment);
int floodprot_nickchange(Client *client, char *oldnick); int floodprot_nickchange(Client *client, MessageTag *mtags, char *oldnick);
int floodprot_chanmode_del(Channel *channel, int m); int floodprot_chanmode_del(Channel *channel, int m);
void memberflood_free(ModData *md); void memberflood_free(ModData *md);
int floodprot_stats(Client *client, char *flag); int floodprot_stats(Client *client, char *flag);
@ -596,7 +596,7 @@ char *cmodef_get_param(void *r_in)
/** Convert parameter to something proper. /** Convert parameter to something proper.
* NOTE: client may be NULL if called for e.g. set::modes-on-join * NOTE: client may be NULL if called for e.g. set::modes-on-join
*/ */
char *cmodef_conv_param(char *param_in, Client *client) char *cmodef_conv_param(char *param_in, Client *client, Channel *channel)
{ {
static char retbuf[256]; static char retbuf[256];
char param[256]; char param[256];
@ -753,7 +753,7 @@ int floodprot_join(Client *client, Channel *channel, MessageTag *mtags, char *pa
(client->srvptr->serv->boottime && (TStime() - client->srvptr->serv->boottime >= MODEF_BOOT_DELAY)) && (client->srvptr->serv->boottime && (TStime() - client->srvptr->serv->boottime >= MODEF_BOOT_DELAY)) &&
!IsULine(client)) !IsULine(client))
{ {
do_floodprot(channel, client, FLD_JOIN); do_floodprot(channel, client, CHFLD_JOIN);
} }
return 0; return 0;
} }
@ -842,7 +842,7 @@ int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *
chp = (ChannelFloodProtection *)GETPARASTRUCT(channel, 'f'); chp = (ChannelFloodProtection *)GETPARASTRUCT(channel, 'f');
if (!chp || !(chp->limit[FLD_TEXT] || chp->limit[FLD_REPEAT])) if (!chp || !(chp->limit[CHFLD_TEXT] || chp->limit[CHFLD_REPEAT]))
return HOOK_CONTINUE; return HOOK_CONTINUE;
if (moddata_membership(mb, mdflood).ptr == NULL) if (moddata_membership(mb, mdflood).ptr == NULL)
@ -859,7 +859,7 @@ int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *
memberflood->firstmsg = TStime(); memberflood->firstmsg = TStime();
memberflood->nmsg = 1; memberflood->nmsg = 1;
memberflood->nmsg_repeat = 1; memberflood->nmsg_repeat = 1;
if (chp->limit[FLD_REPEAT]) if (chp->limit[CHFLD_REPEAT])
{ {
memberflood->lastmsg = gen_floodprot_msghash(*msg); memberflood->lastmsg = gen_floodprot_msghash(*msg);
memberflood->prevmsg = 0; memberflood->prevmsg = 0;
@ -868,7 +868,7 @@ int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *
} }
/* Anti-repeat ('r') */ /* Anti-repeat ('r') */
if (chp->limit[FLD_REPEAT]) if (chp->limit[CHFLD_REPEAT])
{ {
msghash = gen_floodprot_msghash(*msg); msghash = gen_floodprot_msghash(*msg);
if (memberflood->lastmsg) if (memberflood->lastmsg)
@ -876,7 +876,7 @@ int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *
if ((memberflood->lastmsg == msghash) || (memberflood->prevmsg == msghash)) if ((memberflood->lastmsg == msghash) || (memberflood->prevmsg == msghash))
{ {
memberflood->nmsg_repeat++; memberflood->nmsg_repeat++;
if (memberflood->nmsg_repeat > chp->limit[FLD_REPEAT]) if (memberflood->nmsg_repeat > chp->limit[CHFLD_REPEAT])
is_flooding_repeat = 1; is_flooding_repeat = 1;
} }
memberflood->prevmsg = memberflood->lastmsg; memberflood->prevmsg = memberflood->lastmsg;
@ -884,11 +884,11 @@ int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *
memberflood->lastmsg = msghash; memberflood->lastmsg = msghash;
} }
if (chp->limit[FLD_TEXT]) if (chp->limit[CHFLD_TEXT])
{ {
/* increase msgs */ /* increase msgs */
memberflood->nmsg++; memberflood->nmsg++;
if (memberflood->nmsg > chp->limit[FLD_TEXT]) if (memberflood->nmsg > chp->limit[CHFLD_TEXT])
is_flooding_text = 1; is_flooding_text = 1;
} }
@ -903,11 +903,11 @@ int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *
if (is_flooding_repeat) if (is_flooding_repeat)
{ {
snprintf(errbuf, sizeof(errbuf), "Flooding (Your last message is too similar to previous ones)"); snprintf(errbuf, sizeof(errbuf), "Flooding (Your last message is too similar to previous ones)");
flood_type = FLD_REPEAT; flood_type = CHFLD_REPEAT;
} else } else
{ {
snprintf(errbuf, sizeof(errbuf), "Flooding (Limit is %i lines per %i seconds)", chp->limit[FLD_TEXT], chp->per); snprintf(errbuf, sizeof(errbuf), "Flooding (Limit is %i lines per %i seconds)", chp->limit[CHFLD_TEXT], chp->per);
flood_type = FLD_TEXT; flood_type = CHFLD_TEXT;
} }
if (chp->action[flood_type] == 'd') if (chp->action[flood_type] == 'd')
@ -952,10 +952,10 @@ int floodprot_post_chanmsg(Client *client, Channel *channel, int sendflags, int
/* HINT: don't be so stupid to reorder the items in the if's below.. you'll break things -- Syzop. */ /* HINT: don't be so stupid to reorder the items in the if's below.. you'll break things -- Syzop. */
do_floodprot(channel, client, FLD_MSG); do_floodprot(channel, client, CHFLD_MSG);
if ((text[0] == '\001') && strncmp(text+1, "ACTION ", 7)) if ((text[0] == '\001') && strncmp(text+1, "ACTION ", 7))
do_floodprot(channel, client, FLD_CTCP); do_floodprot(channel, client, CHFLD_CTCP);
return 0; return 0;
} }
@ -963,11 +963,11 @@ int floodprot_post_chanmsg(Client *client, Channel *channel, int sendflags, int
int floodprot_knock(Client *client, Channel *channel, MessageTag *mtags, char *comment) int floodprot_knock(Client *client, Channel *channel, MessageTag *mtags, char *comment)
{ {
if (IsFloodLimit(channel) && !IsULine(client)) if (IsFloodLimit(channel) && !IsULine(client))
do_floodprot(channel, client, FLD_KNOCK); do_floodprot(channel, client, CHFLD_KNOCK);
return 0; return 0;
} }
int floodprot_nickchange(Client *client, char *oldnick) int floodprot_nickchange(Client *client, MessageTag *mtags, char *oldnick)
{ {
Membership *mp; Membership *mp;
@ -980,7 +980,7 @@ int floodprot_nickchange(Client *client, char *oldnick)
if (channel && IsFloodLimit(channel) && if (channel && IsFloodLimit(channel) &&
!(mp->flags & (CHFL_CHANOP|CHFL_VOICE|CHFL_CHANOWNER|CHFL_HALFOP|CHFL_CHANADMIN))) !(mp->flags & (CHFL_CHANOP|CHFL_VOICE|CHFL_CHANOWNER|CHFL_HALFOP|CHFL_CHANADMIN)))
{ {
do_floodprot(channel, client, FLD_NICK); do_floodprot(channel, client, CHFLD_NICK);
} }
} }
return 0; return 0;
@ -1001,27 +1001,27 @@ int floodprot_chanmode_del(Channel *channel, int modechar)
switch(modechar) switch(modechar)
{ {
case 'C': case 'C':
chp->counter[FLD_CTCP] = 0; chp->counter[CHFLD_CTCP] = 0;
break; break;
case 'N': case 'N':
chp->counter[FLD_NICK] = 0; chp->counter[CHFLD_NICK] = 0;
break; break;
case 'm': case 'm':
chp->counter[FLD_MSG] = 0; chp->counter[CHFLD_MSG] = 0;
chp->counter[FLD_CTCP] = 0; chp->counter[CHFLD_CTCP] = 0;
break; break;
case 'K': case 'K':
chp->counter[FLD_KNOCK] = 0; chp->counter[CHFLD_KNOCK] = 0;
break; break;
case 'i': case 'i':
chp->counter[FLD_JOIN] = 0; chp->counter[CHFLD_JOIN] = 0;
break; break;
case 'M': case 'M':
chp->counter[FLD_MSG] = 0; chp->counter[CHFLD_MSG] = 0;
chp->counter[FLD_CTCP] = 0; chp->counter[CHFLD_CTCP] = 0;
break; break;
case 'R': case 'R':
chp->counter[FLD_JOIN] = 0; chp->counter[CHFLD_JOIN] = 0;
break; break;
default: default:
break; break;

View File

@ -19,10 +19,12 @@ struct ConfigHistoryExt {
int lines; /**< number of lines */ int lines; /**< number of lines */
long time; /**< seconds */ long time; /**< seconds */
}; };
struct { typedef struct cfgstruct cfgstruct;
struct cfgstruct {
ConfigHistoryExt playback_on_join; /**< Maximum number of lines & time to playback on-join */ ConfigHistoryExt playback_on_join; /**< Maximum number of lines & time to playback on-join */
ConfigHistoryExt max_storage_per_channel; /**< Maximum number of lines & time to record */ ConfigHistoryExt max_storage_per_channel_registered; /**< Maximum number of lines & time to record for +r channels*/
} cfg; ConfigHistoryExt max_storage_per_channel_unregistered; /**< Maximum number of lines & time to record for -r channels */
};
typedef struct HistoryChanMode HistoryChanMode; typedef struct HistoryChanMode HistoryChanMode;
struct HistoryChanMode { struct HistoryChanMode {
@ -30,29 +32,38 @@ struct HistoryChanMode {
unsigned long max_time; /**< Maximum number of time (in seconds) to record */ unsigned long max_time; /**< Maximum number of time (in seconds) to record */
}; };
/* Global variables */
Cmode_t EXTMODE_HISTORY = 0L; Cmode_t EXTMODE_HISTORY = 0L;
static cfgstruct cfg;
static cfgstruct test;
#define HistoryEnabled(channel) (channel->mode.extmode & EXTMODE_HISTORY) #define HistoryEnabled(channel) (channel->mode.extmode & EXTMODE_HISTORY)
/* Forward declarations */ /* Forward declarations */
static void init_config(void); static void init_config(cfgstruct *cfg);
int history_config_test(ConfigFile *, ConfigEntry *, int, int *); int history_config_test(ConfigFile *, ConfigEntry *, int, int *);
int history_config_posttest(int *);
int history_config_run(ConfigFile *, ConfigEntry *, int); int history_config_run(ConfigFile *, ConfigEntry *, int);
int history_chanmode_change(Client *client, Channel *channel, MessageTag *mtags, char *modebuf, char *parabuf, time_t sendts, int samode); int history_chanmode_change(Client *client, Channel *channel, MessageTag *mtags, char *modebuf, char *parabuf, time_t sendts, int samode);
static int compare_history_modes(HistoryChanMode *a, HistoryChanMode *b); static int compare_history_modes(HistoryChanMode *a, HistoryChanMode *b);
int history_chanmode_is_ok(Client *client, Channel *channel, char mode, char *para, int type, int what); int history_chanmode_is_ok(Client *client, Channel *channel, char mode, char *para, int type, int what);
void *history_chanmode_put_param(void *r_in, char *param); void *history_chanmode_put_param(void *r_in, char *param);
char *history_chanmode_get_param(void *r_in); char *history_chanmode_get_param(void *r_in);
char *history_chanmode_conv_param(char *param, Client *client); char *history_chanmode_conv_param(char *param, Client *client, Channel *channel);
void history_chanmode_free_param(void *r); void history_chanmode_free_param(void *r);
void *history_chanmode_dup_struct(void *r_in); void *history_chanmode_dup_struct(void *r_in);
int history_chanmode_sjoin_check(Channel *channel, void *ourx, void *theirx); int history_chanmode_sjoin_check(Channel *channel, void *ourx, void *theirx);
int history_channel_destroy(Channel *channel, int *should_destroy); int history_channel_destroy(Channel *channel, int *should_destroy);
int history_chanmsg(Client *client, Channel *channel, int sendflags, int prefix, char *target, MessageTag *mtags, char *text, SendType sendtype); int history_chanmsg(Client *client, Channel *channel, int sendflags, int prefix, char *target, MessageTag *mtags, char *text, SendType sendtype);
int history_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]); int history_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]);
CMD_OVERRIDE_FUNC(override_mode);
MOD_TEST() MOD_TEST()
{ {
init_config(&test);
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, history_config_test); HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, history_config_test);
HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, history_config_posttest);
return MOD_SUCCESS; return MOD_SUCCESS;
} }
@ -75,7 +86,7 @@ MOD_INIT()
creq.sjoin_check = history_chanmode_sjoin_check; creq.sjoin_check = history_chanmode_sjoin_check;
CmodeAdd(modinfo->handle, creq, &EXTMODE_HISTORY); CmodeAdd(modinfo->handle, creq, &EXTMODE_HISTORY);
init_config(); init_config(&cfg);
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, history_config_run); HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, history_config_run);
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_CHANMODE, 0, history_chanmode_change); HookAdd(modinfo->handle, HOOKTYPE_LOCAL_CHANMODE, 0, history_chanmode_change);
@ -88,6 +99,11 @@ MOD_INIT()
MOD_LOAD() MOD_LOAD()
{ {
CommandOverrideAdd(modinfo->handle, "MODE", override_mode);
CommandOverrideAdd(modinfo->handle, "SVSMODE", override_mode);
CommandOverrideAdd(modinfo->handle, "SVS2MODE", override_mode);
CommandOverrideAdd(modinfo->handle, "SAMODE", override_mode);
CommandOverrideAdd(modinfo->handle, "SJOIN", override_mode);
return MOD_SUCCESS; return MOD_SUCCESS;
} }
@ -96,14 +112,16 @@ MOD_UNLOAD()
return MOD_SUCCESS; return MOD_SUCCESS;
} }
static void init_config(void) static void init_config(cfgstruct *cfg)
{ {
/* Set default values */ /* Set default values */
memset(&cfg, 0, sizeof(cfg)); memset(cfg, 0, sizeof(cfgstruct));
cfg.playback_on_join.lines = 15; cfg->playback_on_join.lines = 15;
cfg.playback_on_join.time = 86400; cfg->playback_on_join.time = 86400;
cfg.max_storage_per_channel.lines = 200; cfg->max_storage_per_channel_unregistered.lines = 200;
cfg.max_storage_per_channel.time = 86400*7; cfg->max_storage_per_channel_unregistered.time = 86400*31;
cfg->max_storage_per_channel_registered.lines = 5000;
cfg->max_storage_per_channel_registered.time = 86400*31;
} }
#define CheckNull(x) if ((!(x)->ce_vardata) || (!(*((x)->ce_vardata)))) { config_error("%s:%i: missing parameter", (x)->ce_fileptr->cf_filename, (x)->ce_varlinenum); errors++; continue; } #define CheckNull(x) if ((!(x)->ce_vardata) || (!(*((x)->ce_vardata)))) { config_error("%s:%i: missing parameter", (x)->ce_fileptr->cf_filename, (x)->ce_varlinenum); errors++; continue; }
@ -111,9 +129,9 @@ static void init_config(void)
int history_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) int history_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
{ {
int errors = 0; int errors = 0;
ConfigEntry *cep, *cepp, *cep4; ConfigEntry *cep, *cepp, *cep4, *cep5;
int on_join_lines=0, maximum_storage_lines=0; int on_join_lines=0, maximum_storage_lines_registered=0, maximum_storage_lines_unregistered=0;
long on_join_time=0L, maximum_storage_time=0L; long on_join_time=0L, maximum_storage_time_registered=0L, maximum_storage_time_unregistered=0L;
/* We only care about set::history */ /* We only care about set::history */
if ((type != CONFIG_SET) || strcmp(ce->ce_varname, "history")) if ((type != CONFIG_SET) || strcmp(ce->ce_varname, "history"))
@ -134,29 +152,29 @@ int history_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
int v; int v;
CheckNull(cep4); CheckNull(cep4);
v = atoi(cep4->ce_vardata); v = atoi(cep4->ce_vardata);
if ((v < 1) || (v > 1000000000)) if ((v < 0) || (v > 1000))
{ {
config_error("%s:%i: set::history::channel::playback-on-join::lines must be between 1 and 1000. " config_error("%s:%i: set::history::channel::playback-on-join::lines must be between 0 and 1000. "
"Recommended values are 10-50. Got: %d.", "Recommended values are 10-50. Got: %d.",
cep4->ce_fileptr->cf_filename, cep4->ce_varlinenum, v); cep4->ce_fileptr->cf_filename, cep4->ce_varlinenum, v);
errors++; errors++;
continue; continue;
} }
on_join_lines = v; test.playback_on_join.lines = v;
} else } else
if (!strcmp(cep4->ce_varname, "time")) if (!strcmp(cep4->ce_varname, "time"))
{ {
long v; long v;
CheckNull(cep4); CheckNull(cep4);
v = config_checkval(cep4->ce_vardata, CFG_TIME); v = config_checkval(cep4->ce_vardata, CFG_TIME);
if (v < 1) if (v < 0)
{ {
config_error("%s:%i: set::history::channel::playback-on-join::time must be a positive number.", config_error("%s:%i: set::history::channel::playback-on-join::time must be zero or more.",
cep4->ce_fileptr->cf_filename, cep4->ce_varlinenum); cep4->ce_fileptr->cf_filename, cep4->ce_varlinenum);
errors++; errors++;
continue; continue;
} }
on_join_time = v; test.playback_on_join.time = v;
} else } else
{ {
config_error_unknown(cep4->ce_fileptr->cf_filename, config_error_unknown(cep4->ce_fileptr->cf_filename,
@ -169,45 +187,127 @@ int history_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
{ {
for (cep4 = cepp->ce_entries; cep4; cep4 = cep4->ce_next) for (cep4 = cepp->ce_entries; cep4; cep4 = cep4->ce_next)
{ {
if (!strcmp(cep4->ce_varname, "lines")) if (!strcmp(cep4->ce_varname, "registered"))
{ {
int v; for (cep5 = cep4->ce_entries; cep5; cep5 = cep5->ce_next)
CheckNull(cep4);
v = atoi(cep4->ce_vardata);
if (v < 1)
{ {
config_error("%s:%i: set::history::channel::max-storage-per-channel::lines must be a positive number.", if (!strcmp(cep5->ce_varname, "lines"))
cep4->ce_fileptr->cf_filename, cep4->ce_varlinenum); {
errors++; int v;
continue; CheckNull(cep5);
v = atoi(cep5->ce_vardata);
if (v < 1)
{
config_error("%s:%i: set::history::channel::max-storage-per-channel::registered::lines must be a positive number.",
cep5->ce_fileptr->cf_filename, cep5->ce_varlinenum);
errors++;
continue;
}
test.max_storage_per_channel_registered.lines = v;
} else
if (!strcmp(cep5->ce_varname, "time"))
{
long v;
CheckNull(cep5);
v = config_checkval(cep5->ce_vardata, CFG_TIME);
if (v < 1)
{
config_error("%s:%i: set::history::channel::max-storage-per-channel::registered::time must be a positive number.",
cep5->ce_fileptr->cf_filename, cep5->ce_varlinenum);
errors++;
continue;
}
test.max_storage_per_channel_registered.time = v;
} else
{
config_error_unknown(cep5->ce_fileptr->cf_filename,
cep5->ce_varlinenum, "set::history::channel::max-storage-per-channel::registered", cep5->ce_varname);
errors++;
}
} }
maximum_storage_lines = v;
} else } else
if (!strcmp(cep4->ce_varname, "time")) if (!strcmp(cep4->ce_varname, "unregistered"))
{ {
long v; for (cep5 = cep4->ce_entries; cep5; cep5 = cep5->ce_next)
CheckNull(cep4);
v = config_checkval(cep4->ce_vardata, CFG_TIME);
if (v < 1)
{ {
config_error("%s:%i: set::history::channel::max-storage-per-channel::time must be a positive number.", if (!strcmp(cep5->ce_varname, "lines"))
cep4->ce_fileptr->cf_filename, cep4->ce_varlinenum); {
errors++; int v;
continue; CheckNull(cep5);
v = atoi(cep5->ce_vardata);
if (v < 1)
{
config_error("%s:%i: set::history::channel::max-storage-per-channel::unregistered::lines must be a positive number.",
cep5->ce_fileptr->cf_filename, cep5->ce_varlinenum);
errors++;
continue;
}
test.max_storage_per_channel_unregistered.lines = v;
} else
if (!strcmp(cep5->ce_varname, "time"))
{
long v;
CheckNull(cep5);
v = config_checkval(cep5->ce_vardata, CFG_TIME);
if (v < 1)
{
config_error("%s:%i: set::history::channel::max-storage-per-channel::unregistered::time must be a positive number.",
cep5->ce_fileptr->cf_filename, cep5->ce_varlinenum);
errors++;
continue;
}
test.max_storage_per_channel_unregistered.time = v;
} else
{
config_error_unknown(cep5->ce_fileptr->cf_filename,
cep5->ce_varlinenum, "set::history::channel::max-storage-per-channel::unregistered", cep5->ce_varname);
errors++;
}
} }
maximum_storage_time = v;
} else } else
{ {
config_error_unknown(cep4->ce_fileptr->cf_filename, config_error_unknown(cep->ce_fileptr->cf_filename,
cep4->ce_varlinenum, "set::history::channel::max-storage-per-channel", cep4->ce_varname); cep->ce_varlinenum, "set::history::max-storage-per-channel", cep->ce_varname);
errors++; errors++;
} }
} }
} else } else
{ {
config_error_unknown(cepp->ce_fileptr->cf_filename, /* hmm.. I don't like this method. but I just quickly copied it from CONFIG_ALLOW for now... */
cepp->ce_varlinenum, "set::history::channel", cepp->ce_varname); int used = 0;
errors++; Hook *h;
for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next)
{
int value, errs = 0;
if (h->owner && !(h->owner->flags & MODFLAG_TESTING)
&& !(h->owner->options & MOD_OPT_PERM))
continue;
value = (*(h->func.intfunc))(cf, cepp, CONFIG_SET_HISTORY_CHANNEL, &errs);
if (value == 2)
used = 1;
if (value == 1)
{
used = 1;
break;
}
if (value == -1)
{
used = 1;
errors += errs;
break;
}
if (value == -2)
{
used = 1;
errors += errs;
}
}
if (!used)
{
config_error_unknown(cepp->ce_fileptr->cf_filename,
cepp->ce_varlinenum, "set::history::channel", cepp->ce_varname);
errors++;
}
} }
} }
} else { } else {
@ -217,23 +317,25 @@ int history_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
} }
} }
if ((on_join_time && maximum_storage_time) && (on_join_time > maximum_storage_time)) *errs = errors;
{ return errors ? -1 : 1;
config_error("set::history::channel::playback-on-join::time cannot be higher than set::history::channel::max-storage-per-channel::time. Either set the playback-on-join::time lower or the maximum::time higher."); }
errors++;
} int history_config_posttest(int *errs)
if ((on_join_lines && maximum_storage_lines) && (on_join_lines > maximum_storage_lines)) {
{ int errors = 0;
config_error("set::history::channel::playback-on-join::lines cannot be higher than set::history::channel::max-storage-per-channel::lines. Either set the playback-on-join::lines lower or the maximum::lines higher.");
errors++; /* We could check here for on join lines / on join time being bigger than max storage but..
} * not really important.
*/
*errs = errors; *errs = errors;
return errors ? -1 : 1; return errors ? -1 : 1;
} }
int history_config_run(ConfigFile *cf, ConfigEntry *ce, int type) int history_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
{ {
ConfigEntry *cep, *cepp, *cep4; ConfigEntry *cep, *cepp, *cep4, *cep5;
if ((type != CONFIG_SET) || strcmp(ce->ce_varname, "history")) if ((type != CONFIG_SET) || strcmp(ce->ce_varname, "history"))
return 0; return 0;
@ -262,15 +364,44 @@ int history_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
{ {
for (cep4 = cepp->ce_entries; cep4; cep4 = cep4->ce_next) for (cep4 = cepp->ce_entries; cep4; cep4 = cep4->ce_next)
{ {
if (!strcmp(cep4->ce_varname, "lines")) if (!strcmp(cep4->ce_varname, "registered"))
{ {
cfg.max_storage_per_channel.lines = atoi(cep4->ce_vardata); for (cep5 = cep4->ce_entries; cep5; cep5 = cep5->ce_next)
{
if (!strcmp(cep5->ce_varname, "lines"))
{
cfg.max_storage_per_channel_registered.lines = atoi(cep5->ce_vardata);
} else
if (!strcmp(cep5->ce_varname, "time"))
{
cfg.max_storage_per_channel_registered.time = config_checkval(cep5->ce_vardata, CFG_TIME);
}
}
} else } else
if (!strcmp(cep4->ce_varname, "time")) if (!strcmp(cep4->ce_varname, "unregistered"))
{ {
cfg.max_storage_per_channel.time = config_checkval(cep4->ce_vardata, CFG_TIME); for (cep5 = cep4->ce_entries; cep5; cep5 = cep5->ce_next)
{
if (!strcmp(cep5->ce_varname, "lines"))
{
cfg.max_storage_per_channel_unregistered.lines = atoi(cep5->ce_vardata);
} else
if (!strcmp(cep5->ce_varname, "time"))
{
cfg.max_storage_per_channel_unregistered.time = config_checkval(cep5->ce_vardata, CFG_TIME);
}
}
} }
} }
} else
{
Hook *h;
for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next)
{
int value = (*(h->func.intfunc))(cf, cepp, CONFIG_SET_HISTORY_CHANNEL);
if (value == 1)
break;
}
} }
} }
} }
@ -284,7 +415,7 @@ int history_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
* @param lines: The number of lines (the X in +H X:Y) * @param lines: The number of lines (the X in +H X:Y)
* @param t: The time value (the Y in +H X:Y) * @param t: The time value (the Y in +H X:Y)
*/ */
int history_parse_chanmode(char *param, int *lines, long *t) int history_parse_chanmode(Channel *channel, char *param, int *lines, long *t)
{ {
char buf[64], *p, *q; char buf[64], *p, *q;
char contains_non_digit = 0; char contains_non_digit = 0;
@ -327,12 +458,20 @@ int history_parse_chanmode(char *param, int *lines, long *t)
return 0; return 0;
/* Check imposed configuration limits... */ /* Check imposed configuration limits... */
if (*lines > cfg.max_storage_per_channel.lines) if (!channel || has_channel_mode(channel, 'r'))
*lines = cfg.max_storage_per_channel.lines; {
if (*lines > cfg.max_storage_per_channel_registered.lines)
*lines = cfg.max_storage_per_channel_registered.lines;
if (*t > cfg.max_storage_per_channel.time) if (*t > cfg.max_storage_per_channel_registered.time)
*t = cfg.max_storage_per_channel.time; *t = cfg.max_storage_per_channel_registered.time;
} else {
if (*lines > cfg.max_storage_per_channel_unregistered.lines)
*lines = cfg.max_storage_per_channel_unregistered.lines;
if (*t > cfg.max_storage_per_channel_unregistered.time)
*t = cfg.max_storage_per_channel_unregistered.time;
}
return 1; return 1;
} }
@ -355,7 +494,7 @@ int history_chanmode_is_ok(Client *client, Channel *channel, char mode, char *pa
int lines = 0; int lines = 0;
long t = 0L; long t = 0L;
if (!history_parse_chanmode(param, &lines, &t)) if (!history_parse_chanmode(channel, param, &lines, &t))
{ {
sendnumeric(client, ERR_CANNOTCHANGECHANMODE, 'H', "Invalid syntax for MODE +H. Use +H lines:period. The period must be in minutes (eg: 10) or a time value (eg: 1h)."); sendnumeric(client, ERR_CANNOTCHANGECHANMODE, 'H', "Invalid syntax for MODE +H. Use +H lines:period. The period must be in minutes (eg: 10) or a time value (eg: 1h).");
return EX_DENY; return EX_DENY;
@ -369,19 +508,37 @@ int history_chanmode_is_ok(Client *client, Channel *channel, char mode, char *pa
return EX_DENY; return EX_DENY;
} }
static void history_chanmode_helper(char *buf, size_t bufsize, int lines, long t)
{
if ((t % 86400) == 0)
{
/* Can be represented in full days, eg "1d" */
snprintf(buf, bufsize, "%d:%ldd", lines, t / 86400);
} else
if ((t % 3600) == 0)
{
/* Can be represented in hours, eg "8h" */
snprintf(buf, bufsize, "%d:%ldh", lines, t / 3600);
} else
{
/* Otherwise, stick to minutes */
snprintf(buf, bufsize, "%d:%ldm", lines, t / 60);
}
}
/** Convert channel parameter to something proper. /** Convert channel parameter to something proper.
* NOTE: client may be NULL if called for e.g. set::modes-playback-on-join * NOTE: client may be NULL if called for e.g. set::modes-playback-on-join
*/ */
char *history_chanmode_conv_param(char *param, Client *client) char *history_chanmode_conv_param(char *param, Client *client, Channel *channel)
{ {
static char buf[64]; static char buf[64];
int lines = 0; int lines = 0;
long t = 0L; long t = 0L;
if (!history_parse_chanmode(param, &lines, &t)) if (!history_parse_chanmode(channel, param, &lines, &t))
return NULL; return NULL;
snprintf(buf, sizeof(buf), "%d:%ldm", lines, t / 60); history_chanmode_helper(buf, sizeof(buf), lines, t);
return buf; return buf;
} }
@ -392,7 +549,7 @@ void *history_chanmode_put_param(void *mode_in, char *param)
int lines = 0; int lines = 0;
long t = 0L; long t = 0L;
if (!history_parse_chanmode(param, &lines, &t)) if (!history_parse_chanmode(NULL, param, &lines, &t))
return NULL; return NULL;
if (!h) if (!h)
@ -416,13 +573,7 @@ char *history_chanmode_get_param(void *h_in)
if (!h_in) if (!h_in)
return NULL; return NULL;
/* For now we convert the time to minutes for displaying purposes history_chanmode_helper(buf, sizeof(buf), h->max_lines, h->max_time);
* and show it as eg 5:10m.
* In a later release we can have a go at converting to '1h', '1d'
* and such, but not before most people run 5.0.2+ as otherwise you
* get desyncs in channel history retention times.
*/
snprintf(buf, sizeof(buf), "%d:%ldm", h->max_lines, h->max_time / 60);
return buf; return buf;
} }
@ -531,17 +682,117 @@ int history_chanmsg(Client *client, Channel *channel, int sendflags, int prefix,
int history_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]) int history_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[])
{ {
if (!HistoryEnabled(channel)) /* Only for +H channels */
if (!HistoryEnabled(channel) || !cfg.playback_on_join.lines || !cfg.playback_on_join.time)
return 0; return 0;
if (MyUser(client)) /* No history-on-join for clients that implement CHATHISTORY,
* they will pull history themselves if they need it.
*/
if (HasCapability(client, "draft/chathistory") || HasCapability(client, "chathistory"))
return 0;
if (MyUser(client) && can_receive_history(client))
{ {
HistoryFilter filter; HistoryFilter filter;
HistoryResult *r;
memset(&filter, 0, sizeof(filter)); memset(&filter, 0, sizeof(filter));
filter.cmd = HFC_SIMPLE;
filter.last_lines = cfg.playback_on_join.lines; filter.last_lines = cfg.playback_on_join.lines;
filter.last_seconds = cfg.playback_on_join.time; filter.last_seconds = cfg.playback_on_join.time;
history_request(client, channel->chname, &filter); r = history_request(channel->chname, &filter);
if (r)
{
history_send_result(client, r);
free_history_result(r);
}
} }
return 0; return 0;
} }
/** Check if a channel went from +r to -r and adjust +H if needed.
* This does not only override "MODE" but also "SAMODE", "SJOIN" and more.
*/
CMD_OVERRIDE_FUNC(override_mode)
{
Channel *channel;
int had_r = 0;
/* We only bother checking for this corner case if the -r
* comes from a server directly linked to us, this normally
* means: we are the server that services are linked to.
*/
if ((IsServer(client) && client->local) ||
(IsUser(client) && client->srvptr && client->srvptr->local))
{
/* Now check if the channel is currently +r */
if ((parc >= 2) && !BadPtr(parv[1]) && ((channel = find_channel(parv[1], NULL))) &&
has_channel_mode(channel, 'r'))
{
had_r = 1;
}
}
CallCommandOverride(ovr, client, recv_mtags, parc, parv);
/* If..
* - channel was +r
* - re-lookup the channel and check that it still
* exists (as it may have been destroyed)
* - and is now -r
* - and has +H set
* then...
*/
if (had_r &&
((channel = find_channel(parv[1], NULL))) &&
!has_channel_mode(channel, 'r') &&
HistoryEnabled(channel))
{
/* Check if limit is higher than allowed for unregistered channels */
HistoryChanMode *settings = (HistoryChanMode *)GETPARASTRUCT(channel, 'H');
int changed = 0;
if (!settings)
return; /* Weird */
if (settings->max_lines > cfg.max_storage_per_channel_unregistered.lines)
{
settings->max_lines = cfg.max_storage_per_channel_unregistered.lines;
changed = 1;
}
if (settings->max_time > cfg.max_storage_per_channel_unregistered.time)
{
settings->max_time = cfg.max_storage_per_channel_unregistered.time;
changed = 1;
}
if (changed)
{
MessageTag *mtags = NULL;
char *params = history_chanmode_get_param(settings);
if (!params)
return; /* Weird */
strlcpy(modebuf, "+H", sizeof(modebuf));
strlcpy(parabuf, params, sizeof(modebuf));
new_message(&me, NULL, &mtags);
sendto_channel(channel, &me, &me, 0, 0, SEND_LOCAL, mtags,
":%s MODE %s %s %s",
me.name, channel->chname, modebuf, parabuf);
sendto_server(NULL, 0, 0, mtags, ":%s MODE %s %s %s %lld",
me.id, channel->chname, modebuf, parabuf,
(long long)channel->creationtime);
/* Activate this hook just like cmd_mode.c */
RunHook7(HOOKTYPE_REMOTE_CHANMODE, &me, channel, mtags, modebuf, parabuf, 0, 0);
free_message_tags(mtags);
*modebuf = *parabuf = '\0';
}
}
}

View File

@ -48,7 +48,7 @@ typedef enum {
int cmodeL_is_ok(Client *client, Channel *channel, char mode, char *para, int type, int what); int cmodeL_is_ok(Client *client, Channel *channel, char mode, char *para, int type, int what);
void *cmodeL_put_param(void *r_in, char *param); void *cmodeL_put_param(void *r_in, char *param);
char *cmodeL_get_param(void *r_in); char *cmodeL_get_param(void *r_in);
char *cmodeL_conv_param(char *param_in, Client *client); char *cmodeL_conv_param(char *param_in, Client *client, Channel *channel);
void cmodeL_free_param(void *r); void cmodeL_free_param(void *r);
void *cmodeL_dup_struct(void *r_in); void *cmodeL_dup_struct(void *r_in);
int cmodeL_sjoin_check(Channel *channel, void *ourx, void *theirx); int cmodeL_sjoin_check(Channel *channel, void *ourx, void *theirx);
@ -171,7 +171,7 @@ char *cmodeL_get_param(void *r_in)
/** Convert parameter to something proper. /** Convert parameter to something proper.
* NOTE: client may be NULL * NOTE: client may be NULL
*/ */
char *cmodeL_conv_param(char *param, Client *client) char *cmodeL_conv_param(char *param, Client *client, Channel *channel)
{ {
char *p; char *p;

View File

@ -35,14 +35,14 @@ ModuleHeader MOD_HEADER = {
do { \ do { \
sendto_realops_and_log("[channeldb] Error writing to temporary database file " \ sendto_realops_and_log("[channeldb] Error writing to temporary database file " \
"'%s': %s (DATABASE NOT SAVED)", \ "'%s': %s (DATABASE NOT SAVED)", \
fname, strerror(errno)); \ fname, unrealdb_get_error_string()); \
} while(0) } while(0)
#define W_SAFE(x) \ #define W_SAFE(x) \
do { \ do { \
if (!(x)) { \ if (!(x)) { \
WARN_WRITE_ERROR(tmpfname); \ WARN_WRITE_ERROR(tmpfname); \
fclose(fd); \ unrealdb_close(db); \
return 0; \ return 0; \
} \ } \
} while(0) } while(0)
@ -55,43 +55,53 @@ ModuleHeader MOD_HEADER = {
} \ } \
} while(0) } while(0)
/* Structs */
struct cfgstruct {
char *database;
char *db_secret;
};
/* Forward declarations */ /* Forward declarations */
void channeldb_moddata_free(ModData *md); void channeldb_moddata_free(ModData *md);
void setcfg(void); void setcfg(struct cfgstruct *cfg);
void freecfg(void); void freecfg(struct cfgstruct *cfg);
int channeldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); int channeldb_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
int channeldb_configrun(ConfigFile *cf, ConfigEntry *ce, int type); int channeldb_config_posttest(int *errs);
int channeldb_config_run(ConfigFile *cf, ConfigEntry *ce, int type);
EVENT(write_channeldb_evt); EVENT(write_channeldb_evt);
int write_channeldb(void); int write_channeldb(void);
int write_channel_entry(FILE *fd, const char *tmpfname, Channel *channel); int write_channel_entry(UnrealDB *db, const char *tmpfname, Channel *channel);
int read_channeldb(void); int read_channeldb(void);
static void set_channel_mode(Channel *channel, char *modes, char *parameters); static void set_channel_mode(Channel *channel, char *modes, char *parameters);
/* Global variables */ /* Global variables */
static uint32_t channeldb_version = CHANNELDB_VERSION; static uint32_t channeldb_version = CHANNELDB_VERSION;
struct cfgstruct {
char *database;
};
static struct cfgstruct cfg; static struct cfgstruct cfg;
static struct cfgstruct test;
static long channeldb_next_event = 0; static long channeldb_next_event = 0;
MOD_TEST() MOD_TEST()
{ {
memset(&cfg, 0, sizeof(cfg)); memset(&cfg, 0, sizeof(cfg));
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, channeldb_configtest); memset(&test, 0, sizeof(test));
setcfg(&test);
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, channeldb_config_test);
HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, channeldb_config_posttest);
return MOD_SUCCESS; return MOD_SUCCESS;
} }
MOD_INIT() MOD_INIT()
{ {
MARK_AS_OFFICIAL_MODULE(modinfo); MARK_AS_OFFICIAL_MODULE(modinfo);
/* We must unload early, when all channel modes and such are still in place: */
ModuleSetOptions(modinfo->handle, MOD_OPT_UNLOAD_PRIORITY, -99999999);
LoadPersistentLong(modinfo, channeldb_next_event); LoadPersistentLong(modinfo, channeldb_next_event);
setcfg(); setcfg(&cfg);
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, channeldb_configrun); HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, channeldb_config_run);
return MOD_SUCCESS; return MOD_SUCCESS;
} }
@ -122,7 +132,10 @@ MOD_LOAD()
MOD_UNLOAD() MOD_UNLOAD()
{ {
freecfg(); if (loop.ircd_terminating)
write_channeldb();
freecfg(&test);
freecfg(&cfg);
SavePersistentLong(modinfo, channeldb_next_event); SavePersistentLong(modinfo, channeldb_next_event);
return MOD_SUCCESS; return MOD_SUCCESS;
} }
@ -133,19 +146,20 @@ void channeldb_moddata_free(ModData *md)
md->i = 0; md->i = 0;
} }
void setcfg(void) void setcfg(struct cfgstruct *cfg)
{ {
// Default: data/channel.db // Default: data/channel.db
safe_strdup(cfg.database, "channel.db"); safe_strdup(cfg->database, "channel.db");
convert_to_absolute_path(&cfg.database, PERMDATADIR); convert_to_absolute_path(&cfg->database, PERMDATADIR);
} }
void freecfg(void) void freecfg(struct cfgstruct *cfg)
{ {
safe_free(cfg.database); safe_free(cfg->database);
safe_free(cfg->db_secret);
} }
int channeldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) int channeldb_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
{ {
int errors = 0; int errors = 0;
ConfigEntry *cep; ConfigEntry *cep;
@ -159,16 +173,45 @@ int channeldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
for (cep = ce->ce_entries; cep; cep = cep->ce_next) for (cep = ce->ce_entries; cep; cep = cep->ce_next)
{ {
if (!cep->ce_vardata) { if (!cep->ce_vardata)
{
config_error("%s:%i: blank set::channeldb::%s without value", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); config_error("%s:%i: blank set::channeldb::%s without value", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname);
errors++; errors++;
continue; } else
} if (!strcmp(cep->ce_varname, "database"))
if (!strcmp(cep->ce_varname, "database")) { {
convert_to_absolute_path(&cep->ce_vardata, PERMDATADIR); convert_to_absolute_path(&cep->ce_vardata, PERMDATADIR);
continue; safe_strdup(test.database, cep->ce_vardata);
} else
if (!strcmp(cep->ce_varname, "db-secret"))
{
char *err;
if ((err = unrealdb_test_secret(cep->ce_vardata)))
{
config_error("%s:%i: set::channeldb::db-secret: %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, err);
errors++;
continue;
}
safe_strdup(test.db_secret, cep->ce_vardata);
} else
{
config_error("%s:%i: unknown directive set::channeldb::%s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname);
errors++;
} }
config_error("%s:%i: unknown directive set::channeldb::%s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); }
*errs = errors;
return errors ? -1 : 1;
}
int channeldb_config_posttest(int *errs)
{
int errors = 0;
char *errstr;
if (test.database && ((errstr = unrealdb_test_db(test.database, test.db_secret))))
{
config_error("[channeldb] %s", errstr);
errors++; errors++;
} }
@ -176,7 +219,7 @@ int channeldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
return errors ? -1 : 1; return errors ? -1 : 1;
} }
int channeldb_configrun(ConfigFile *cf, ConfigEntry *ce, int type) int channeldb_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
{ {
ConfigEntry *cep; ConfigEntry *cep;
@ -191,6 +234,8 @@ int channeldb_configrun(ConfigFile *cf, ConfigEntry *ce, int type)
{ {
if (!strcmp(cep->ce_varname, "database")) if (!strcmp(cep->ce_varname, "database"))
safe_strdup(cfg.database, cep->ce_vardata); safe_strdup(cfg.database, cep->ce_vardata);
else if (!strcmp(cep->ce_varname, "db-secret"))
safe_strdup(cfg.db_secret, cep->ce_vardata);
} }
return 1; return 1;
} }
@ -206,7 +251,7 @@ EVENT(write_channeldb_evt)
int write_channeldb(void) int write_channeldb(void)
{ {
char tmpfname[512]; char tmpfname[512];
FILE *fd; UnrealDB *db;
Channel *channel; Channel *channel;
int cnt = 0; int cnt = 0;
#ifdef BENCHMARK #ifdef BENCHMARK
@ -217,33 +262,33 @@ int write_channeldb(void)
// Write to a tempfile first, then rename it if everything succeeded // Write to a tempfile first, then rename it if everything succeeded
snprintf(tmpfname, sizeof(tmpfname), "%s.%x.tmp", cfg.database, getrandom32()); snprintf(tmpfname, sizeof(tmpfname), "%s.%x.tmp", cfg.database, getrandom32());
fd = fopen(tmpfname, "wb"); db = unrealdb_open(tmpfname, UNREALDB_MODE_WRITE, cfg.db_secret);
if (!fd) if (!db)
{ {
WARN_WRITE_ERROR(tmpfname); WARN_WRITE_ERROR(tmpfname);
return 0; return 0;
} }
W_SAFE(write_data(fd, &channeldb_version, sizeof(channeldb_version))); W_SAFE(unrealdb_write_int32(db, channeldb_version));
/* First, count +P channels and write the count to the database */ /* First, count +P channels and write the count to the database */
for (channel = channels; channel; channel=channel->nextch) for (channel = channels; channel; channel=channel->nextch)
if (has_channel_mode(channel, 'P')) if (has_channel_mode(channel, 'P'))
cnt++; cnt++;
W_SAFE(write_int64(fd, cnt)); W_SAFE(unrealdb_write_int64(db, cnt));
for (channel = channels; channel; channel=channel->nextch) for (channel = channels; channel; channel=channel->nextch)
{ {
/* We only care about +P (persistent) channels */ /* We only care about +P (persistent) channels */
if (has_channel_mode(channel, 'P')) if (has_channel_mode(channel, 'P'))
{ {
if (!write_channel_entry(fd, tmpfname, channel)) if (!write_channel_entry(db, tmpfname, channel))
return 0; return 0;
} }
} }
// Everything seems to have gone well, attempt to close and rename the tempfile // Everything seems to have gone well, attempt to close and rename the tempfile
if (fclose(fd) != 0) if (!unrealdb_close(db))
{ {
WARN_WRITE_ERROR(tmpfname); WARN_WRITE_ERROR(tmpfname);
return 0; return 0;
@ -266,7 +311,7 @@ int write_channeldb(void)
return 1; return 1;
} }
int write_listmode(FILE *fd, const char *tmpfname, Ban *lst) int write_listmode(UnrealDB *db, const char *tmpfname, Ban *lst)
{ {
Ban *l; Ban *l;
int cnt = 0; int cnt = 0;
@ -274,50 +319,50 @@ int write_listmode(FILE *fd, const char *tmpfname, Ban *lst)
/* First count and write the list count */ /* First count and write the list count */
for (l = lst; l; l = l->next) for (l = lst; l; l = l->next)
cnt++; cnt++;
W_SAFE(write_int32(fd, cnt)); W_SAFE(unrealdb_write_int32(db, cnt));
for (l = lst; l; l = l->next) for (l = lst; l; l = l->next)
{ {
/* The entry, setby, seton */ /* The entry, setby, seton */
W_SAFE(write_str(fd, l->banstr)); W_SAFE(unrealdb_write_str(db, l->banstr));
W_SAFE(write_str(fd, l->who)); W_SAFE(unrealdb_write_str(db, l->who));
W_SAFE(write_int64(fd, l->when)); W_SAFE(unrealdb_write_int64(db, l->when));
} }
return 1; return 1;
} }
int write_channel_entry(FILE *fd, const char *tmpfname, Channel *channel) int write_channel_entry(UnrealDB *db, const char *tmpfname, Channel *channel)
{ {
W_SAFE(write_int32(fd, MAGIC_CHANNEL_START)); W_SAFE(unrealdb_write_int32(db, MAGIC_CHANNEL_START));
/* Channel name */ /* Channel name */
W_SAFE(write_str(fd, channel->chname)); W_SAFE(unrealdb_write_str(db, channel->chname));
/* Channel creation time */ /* Channel creation time */
W_SAFE(write_int64(fd, channel->creationtime)); W_SAFE(unrealdb_write_int64(db, channel->creationtime));
/* Topic (topic, setby, seton) */ /* Topic (topic, setby, seton) */
W_SAFE(write_str(fd, channel->topic)); W_SAFE(unrealdb_write_str(db, channel->topic));
W_SAFE(write_str(fd, channel->topic_nick)); W_SAFE(unrealdb_write_str(db, channel->topic_nick));
W_SAFE(write_int64(fd, channel->topic_time)); W_SAFE(unrealdb_write_int64(db, channel->topic_time));
/* Basic channel modes (eg: +sntkl key 55) */ /* Basic channel modes (eg: +sntkl key 55) */
channel_modes(&me, modebuf, parabuf, sizeof(modebuf), sizeof(parabuf), channel); channel_modes(&me, modebuf, parabuf, sizeof(modebuf), sizeof(parabuf), channel);
W_SAFE(write_str(fd, modebuf)); W_SAFE(unrealdb_write_str(db, modebuf));
W_SAFE(write_str(fd, parabuf)); W_SAFE(unrealdb_write_str(db, parabuf));
/* Mode lock */ /* Mode lock */
W_SAFE(write_str(fd, channel->mode_lock)); W_SAFE(unrealdb_write_str(db, channel->mode_lock));
/* List modes (bans, exempts, invex) */ /* List modes (bans, exempts, invex) */
if (!write_listmode(fd, tmpfname, channel->banlist)) if (!write_listmode(db, tmpfname, channel->banlist))
return 0; return 0;
if (!write_listmode(fd, tmpfname, channel->exlist)) if (!write_listmode(db, tmpfname, channel->exlist))
return 0; return 0;
if (!write_listmode(fd, tmpfname, channel->invexlist)) if (!write_listmode(db, tmpfname, channel->invexlist))
return 0; return 0;
W_SAFE(write_int32(fd, MAGIC_CHANNEL_END)); W_SAFE(unrealdb_write_int32(db, MAGIC_CHANNEL_END));
return 1; return 1;
} }
#define R_SAFE(x) \ #define R_SAFE(x) \
do { \ do { \
if (!(x)) { \ if (!(x)) { \
config_warn("[channeldb] Read error from database file '%s' (possible corruption): %s", cfg.database, strerror(errno)); \ config_warn("[channeldb] Read error from database file '%s' (possible corruption): %s", cfg.database, unrealdb_get_error_string()); \
if (e) \ if (e) \
{ \ { \
safe_free(e->banstr); \ safe_free(e->banstr); \
@ -328,21 +373,21 @@ int write_channel_entry(FILE *fd, const char *tmpfname, Channel *channel)
} \ } \
} while(0) } while(0)
int read_listmode(FILE *fd, Ban **lst) int read_listmode(UnrealDB *db, Ban **lst)
{ {
uint32_t total; uint32_t total;
uint64_t when; uint64_t when;
int i; int i;
Ban *e = NULL; Ban *e = NULL;
R_SAFE(read_data(fd, &total, sizeof(total))); R_SAFE(unrealdb_read_int32(db, &total));
for (i = 0; i < total; i++) for (i = 0; i < total; i++)
{ {
e = safe_alloc(sizeof(Ban)); e = safe_alloc(sizeof(Ban));
R_SAFE(read_str(fd, &e->banstr)); R_SAFE(unrealdb_read_str(db, &e->banstr));
R_SAFE(read_str(fd, &e->who)); R_SAFE(unrealdb_read_str(db, &e->who));
R_SAFE(read_data(fd, &when, sizeof(when))); R_SAFE(unrealdb_read_int64(db, &when));
e->when = when; e->when = when;
e->next = *lst; e->next = *lst;
*lst = e; *lst = e;
@ -366,8 +411,8 @@ int read_listmode(FILE *fd, Ban **lst)
#define R_SAFE(x) \ #define R_SAFE(x) \
do { \ do { \
if (!(x)) { \ if (!(x)) { \
config_warn("[channeldb] Read error from database file '%s' (possible corruption): %s", cfg.database, strerror(errno)); \ config_warn("[channeldb] Read error from database file '%s' (possible corruption): %s", cfg.database, unrealdb_get_error_string()); \
fclose(fd); \ unrealdb_close(db); \
FreeChannelEntry(); \ FreeChannelEntry(); \
return 0; \ return 0; \
} \ } \
@ -375,7 +420,7 @@ int read_listmode(FILE *fd, Ban **lst)
int read_channeldb(void) int read_channeldb(void)
{ {
FILE *fd; UnrealDB *db;
uint32_t version; uint32_t version;
int added = 0; int added = 0;
int i; int i;
@ -397,29 +442,41 @@ int read_channeldb(void)
gettimeofday(&tv_alpha, NULL); gettimeofday(&tv_alpha, NULL);
#endif #endif
fd = fopen(cfg.database, "rb"); db = unrealdb_open(cfg.database, UNREALDB_MODE_READ, cfg.db_secret);
if (!fd) if (!db)
{ {
if (errno == ENOENT) if (unrealdb_get_error_code() == UNREALDB_ERROR_FILENOTFOUND)
{ {
/* Database does not exist. Could be first boot */ /* Database does not exist. Could be first boot */
config_warn("[channeldb] No database present at '%s', will start a new one", cfg.database); config_warn("[channeldb] No database present at '%s', will start a new one", cfg.database);
return 1; return 1;
} else { } else
config_warn("[channeldb] Unable to open the database file '%s' for reading: %s", cfg.database, strerror(errno)); if (unrealdb_get_error_code() == UNREALDB_ERROR_NOTCRYPTED)
{
/* Re-open as unencrypted */
db = unrealdb_open(cfg.database, UNREALDB_MODE_READ, NULL);
if (!db)
{
/* This should actually never happen, unless some weird I/O error */
config_warn("[channeldb] Unable to open the database file '%s': %s", cfg.database, unrealdb_get_error_string());
return 0;
}
} else
{
config_warn("[channeldb] Unable to open the database file '%s' for reading: %s", cfg.database, unrealdb_get_error_string());
return 0; return 0;
} }
} }
R_SAFE(read_data(fd, &version, sizeof(version))); R_SAFE(unrealdb_read_int32(db, &version));
if (version > channeldb_version) if (version > channeldb_version)
{ {
config_warn("[channeldb] Database '%s' has a wrong version: expected it to be <= %u but got %u instead", cfg.database, channeldb_version, version); config_warn("[channeldb] Database '%s' has a wrong version: expected it to be <= %u but got %u instead", cfg.database, channeldb_version, version);
fclose(fd); unrealdb_close(db);
return 0; return 0;
} }
R_SAFE(read_data(fd, &count, sizeof(count))); R_SAFE(unrealdb_read_int64(db, &count));
for (i=1; i <= count; i++) for (i=1; i <= count; i++)
{ {
@ -434,20 +491,20 @@ int read_channeldb(void)
mode_lock = NULL; mode_lock = NULL;
Channel *channel; Channel *channel;
R_SAFE(read_data(fd, &magic, sizeof(magic))); R_SAFE(unrealdb_read_int32(db, &magic));
if (magic != MAGIC_CHANNEL_START) if (magic != MAGIC_CHANNEL_START)
{ {
config_error("[channeldb] Corrupt database (%s) - channel magic start is 0x%x. Further reading aborted.", cfg.database, magic); config_error("[channeldb] Corrupt database (%s) - channel magic start is 0x%x. Further reading aborted.", cfg.database, magic);
break; break;
} }
R_SAFE(read_str(fd, &chname)); R_SAFE(unrealdb_read_str(db, &chname));
R_SAFE(read_data(fd, &creationtime, sizeof(creationtime))); R_SAFE(unrealdb_read_int64(db, &creationtime));
R_SAFE(read_str(fd, &topic)); R_SAFE(unrealdb_read_str(db, &topic));
R_SAFE(read_str(fd, &topic_nick)); R_SAFE(unrealdb_read_str(db, &topic_nick));
R_SAFE(read_data(fd, &topic_time, sizeof(topic_time))); R_SAFE(unrealdb_read_int64(db, &topic_time));
R_SAFE(read_str(fd, &modes1)); R_SAFE(unrealdb_read_str(db, &modes1));
R_SAFE(read_str(fd, &modes2)); R_SAFE(unrealdb_read_str(db, &modes2));
R_SAFE(read_str(fd, &mode_lock)); R_SAFE(unrealdb_read_str(db, &mode_lock));
/* If we got this far, we can create/initialize the channel with the above */ /* If we got this far, we can create/initialize the channel with the above */
channel = get_channel(&me, chname, CREATE); channel = get_channel(&me, chname, CREATE);
channel->creationtime = creationtime; channel->creationtime = creationtime;
@ -456,10 +513,10 @@ int read_channeldb(void)
channel->topic_time = topic_time; channel->topic_time = topic_time;
safe_strdup(channel->mode_lock, mode_lock); safe_strdup(channel->mode_lock, mode_lock);
set_channel_mode(channel, modes1, modes2); set_channel_mode(channel, modes1, modes2);
R_SAFE(read_listmode(fd, &channel->banlist)); R_SAFE(read_listmode(db, &channel->banlist));
R_SAFE(read_listmode(fd, &channel->exlist)); R_SAFE(read_listmode(db, &channel->exlist));
R_SAFE(read_listmode(fd, &channel->invexlist)); R_SAFE(read_listmode(db, &channel->invexlist));
R_SAFE(read_data(fd, &magic, sizeof(magic))); R_SAFE(unrealdb_read_int32(db, &magic));
FreeChannelEntry(); FreeChannelEntry();
added++; added++;
if (magic != MAGIC_CHANNEL_END) if (magic != MAGIC_CHANNEL_END)
@ -469,7 +526,7 @@ int read_channeldb(void)
} }
} }
fclose(fd); unrealdb_close(db);
if (added) if (added)
sendto_realops_and_log("[channeldb] Added %d persistent channels (+P)", added); sendto_realops_and_log("[channeldb] Added %d persistent channels (+P)", added);

294
src/modules/chathistory.c Normal file
View File

@ -0,0 +1,294 @@
/* src/modules/chathistory.c - IRCv3 CHATHISTORY command.
* (C) Copyright 2021 Bram Matthys (Syzop) and the UnrealIRCd team
* License: GPLv2
*
* This implements the "CHATHISTORY" command, the CAP and 005 token.
* https://ircv3.net/specs/extensions/chathistory
*/
#include "unrealircd.h"
ModuleHeader MOD_HEADER
= {
"chathistory",
"1.0",
"IRCv3 CHATHISTORY command",
"UnrealIRCd Team",
"unrealircd-5",
};
/* Forward declarations */
CMD_FUNC(cmd_chathistory);
/* Global variables */
long CAP_CHATHISTORY = 0L;
/* TODO: consider moving to config file */
#define CHATHISTORY_LIMIT 50
MOD_INIT()
{
ClientCapabilityInfo c;
MARK_AS_OFFICIAL_MODULE(modinfo);
CommandAdd(modinfo->handle, "CHATHISTORY", cmd_chathistory, MAXPARA, CMD_USER);
memset(&c, 0, sizeof(c));
c.name = "draft/chathistory";
ClientCapabilityAdd(modinfo->handle, &c, &CAP_CHATHISTORY);
return MOD_SUCCESS;
}
MOD_LOAD()
{
ISupportSetFmt(modinfo->handle, "CHATHISTORY", "%d", CHATHISTORY_LIMIT);
return MOD_SUCCESS;
}
MOD_UNLOAD()
{
return MOD_SUCCESS;
}
int chathistory_token(char *str, char *token, char **store)
{
char *p = strchr(str, '=');
if (!p)
return 0;
*p = '\0'; // frag
if (!strcmp(str, token))
{
*p = '='; // restore
*store = strdup(p + 1); // can be \0
return 1;
}
*p = '='; // restore
return 0;
}
static int chathistory_targets_send_line(Client *client, HistoryResult *r, char *batchid)
{
MessageTag *mtags = NULL;
MessageTag *m;
char *ts;
if (!r->log || !((m = find_mtag(r->log->mtags, "time"))) || !m->value)
return 0;
ts = m->value;
if (!BadPtr(batchid))
{
mtags = safe_alloc(sizeof(MessageTag));
mtags->name = strdup("batch");
mtags->value = strdup(batchid);
}
sendto_one(client, mtags, ":%s CHATHISTORY TARGETS %s %s",
me.name, r->object, ts);
if (mtags)
free_message_tags(mtags);
return 1;
}
void chathistory_targets(Client *client, HistoryFilter *filter, int limit)
{
Membership *mp;
HistoryResult *r;
char batch[BATCHLEN+1];
int sent = 0;
batch[0] = '\0';
if (HasCapability(client, "batch"))
{
/* Start a new batch */
generate_batch_id(batch);
sendto_one(client, NULL, ":%s BATCH +%s draft/chathistory-targets", me.name, batch);
}
filter->cmd = HFC_BEFORE;
if (strcmp(filter->timestamp_a, filter->timestamp_b) < 0)
{
/* Swap if needed */
char *swap = filter->timestamp_a;
filter->timestamp_a = filter->timestamp_b;
filter->timestamp_b = swap;
}
filter->limit = 1;
for (mp = client->user->channel; mp; mp = mp->next)
{
Channel *channel = mp->channel;
r = history_request(channel->chname, filter);
if (r->log && chathistory_targets_send_line(client, r, batch))
{
if (++sent >= limit)
break; /* We are done */
}
free_history_result(r);
r = NULL;
}
/* End of batch */
if (*batch)
sendto_one(client, NULL, ":%s BATCH -%s", me.name, batch);
}
CMD_FUNC(cmd_chathistory)
{
HistoryFilter *filter = NULL;
HistoryResult *r = NULL;
Channel *channel;
memset(&filter, 0, sizeof(filter));
/* This command is only for local users */
if (!MyUser(client))
return;
if ((parc < 5) || BadPtr(parv[4]))
{
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS :Insufficient parameters", me.name);
return;
}
if (!HasCapability(client, "server-time"))
{
sendnotice(client, "Your IRC client does not support the 'server-time' capability");
sendnotice(client, "https://ircv3.net/specs/extensions/server-time");
sendnotice(client, "History request refused.");
return;
}
if (!strcmp(parv[1], "TARGETS"))
{
Membership *mp;
int limit;
filter = safe_alloc(sizeof(HistoryFilter));
/* Below this point, instead of 'return', use 'goto end' */
if (!chathistory_token(parv[2], "timestamp", &filter->timestamp_a))
{
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s %s :Invalid parameter, must be timestamp=xxx",
me.name, parv[1], parv[3]);
goto end;
}
if (!chathistory_token(parv[3], "timestamp", &filter->timestamp_b))
{
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s %s :Invalid parameter, must be timestamp=xxx",
me.name, parv[1], parv[4]);
goto end;
}
limit = atoi(parv[4]);
chathistory_targets(client, filter, limit);
goto end;
}
channel = find_channel(parv[2], NULL);
if (!channel || !IsMember(client, channel) || !has_channel_mode(channel, 'H'))
{
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_TARGET %s %s :Messages could not be retrieved",
me.name, parv[1], parv[2]);
return;
}
filter = safe_alloc(sizeof(HistoryFilter));
/* Below this point, instead of 'return', use 'goto end', which takes care of the freeing of 'filter' and 'history' */
if (!strcmp(parv[1], "BEFORE"))
{
filter->cmd = HFC_BEFORE;
if (!chathistory_token(parv[3], "timestamp", &filter->timestamp_a) &&
!chathistory_token(parv[3], "msgid", &filter->msgid_a))
{
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s %s :Invalid parameter, must be timestamp=xxx or msgid=xxx",
me.name, parv[1], parv[3]);
goto end;
}
filter->limit = atoi(parv[4]);
} else
if (!strcmp(parv[1], "AFTER"))
{
filter->cmd = HFC_AFTER;
if (!chathistory_token(parv[3], "timestamp", &filter->timestamp_a) &&
!chathistory_token(parv[3], "msgid", &filter->msgid_a))
{
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s %s :Invalid parameter, must be timestamp=xxx or msgid=xxx",
me.name, parv[1], parv[3]);
goto end;
}
filter->limit = atoi(parv[4]);
} else
if (!strcmp(parv[1], "LATEST"))
{
filter->cmd = HFC_LATEST;
if (!chathistory_token(parv[3], "timestamp", &filter->timestamp_a) &&
!chathistory_token(parv[3], "msgid", &filter->msgid_a) &&
strcmp(parv[3], "*"))
{
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s %s :Invalid parameter, must be timestamp=xxx or msgid=xxx or *",
me.name, parv[1], parv[3]);
goto end;
}
filter->limit = atoi(parv[4]);
} else
if (!strcmp(parv[1], "AROUND"))
{
filter->cmd = HFC_AROUND;
if (!chathistory_token(parv[3], "timestamp", &filter->timestamp_a) &&
!chathistory_token(parv[3], "msgid", &filter->msgid_a))
{
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s %s :Invalid parameter, must be timestamp=xxx or msgid=xxx",
me.name, parv[1], parv[3]);
goto end;
}
filter->limit = atoi(parv[4]);
} else
if (!strcmp(parv[1], "BETWEEN"))
{
filter->cmd = HFC_BETWEEN;
if (BadPtr(parv[5]))
{
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s :Insufficient parameters", parv[1], me.name);
goto end;
}
if (!chathistory_token(parv[3], "timestamp", &filter->timestamp_a) &&
!chathistory_token(parv[3], "msgid", &filter->msgid_a))
{
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s %s :Invalid parameter, must be timestamp=xxx or msgid=xxx",
me.name, parv[1], parv[3]);
goto end;
}
if (!chathistory_token(parv[4], "timestamp", &filter->timestamp_b) &&
!chathistory_token(parv[4], "msgid", &filter->msgid_b))
{
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s %s :Invalid parameter, must be timestamp=xxx or msgid=xxx",
me.name, parv[1], parv[4]);
goto end;
}
filter->limit = atoi(parv[5]);
} else {
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s :Invalid subcommand", me.name, parv[1]);
goto end;
}
if (filter->limit <= 0)
{
sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_PARAMS %s %d :Specified limit is =<0",
me.name, parv[1], filter->limit);
goto end;
}
if (filter->limit > CHATHISTORY_LIMIT)
filter->limit = CHATHISTORY_LIMIT;
if ((r = history_request(channel->chname, filter)))
history_send_result(client, r);
end:
if (filter)
free_history_filter(filter);
if (r)
free_history_result(r);
}

View File

@ -639,7 +639,7 @@ static int can_dcc(Client *client, char *target, Client *targetcli, char *filena
return 0; return 0;
} }
if (match_spamfilter(client, filename, SPAMF_DCC, target, 0, NULL)) if (match_spamfilter(client, filename, SPAMF_DCC, "PRIVMSG", target, 0, NULL))
return 0; return 0;
if ((fl = dcc_isforbidden(client, filename))) if ((fl = dcc_isforbidden(client, filename)))

View File

@ -72,6 +72,7 @@ void history_usage(Client *client)
CMD_FUNC(cmd_history) CMD_FUNC(cmd_history)
{ {
HistoryFilter filter; HistoryFilter filter;
HistoryResult *r;
Channel *channel; Channel *channel;
int lines = HISTORY_LINES_DEFAULT; int lines = HISTORY_LINES_DEFAULT;
@ -115,12 +116,18 @@ CMD_FUNC(cmd_history)
if (!HasCapability(client, "server-time")) if (!HasCapability(client, "server-time"))
{ {
sendnotice(client, "Your IRC client does not support the 'server-time' capability"); sendnotice(client, "Your IRC client does not support the 'server-time' capability");
sendnotice(client, "https://ircv3.net/specs/extensions/server-time-3.2.html"); sendnotice(client, "https://ircv3.net/specs/extensions/server-time");
sendnotice(client, "History request refused."); sendnotice(client, "History request refused.");
return; return;
} }
memset(&filter, 0, sizeof(filter)); memset(&filter, 0, sizeof(filter));
filter.cmd = HFC_SIMPLE;
filter.last_lines = lines; filter.last_lines = lines;
history_request(client, channel->chname, &filter);
if ((r = history_request(channel->chname, &filter)))
{
history_send_result(client, r);
free_history_result(r);
}
} }

File diff suppressed because it is too large Load Diff

View File

@ -22,7 +22,7 @@ ModuleHeader MOD_HEADER
/* Forward declarations */ /* Forward declarations */
int hbn_history_set_limit(char *object, int max_lines, long max_time); int hbn_history_set_limit(char *object, int max_lines, long max_time);
int hbn_history_add(char *object, MessageTag *mtags, char *line); int hbn_history_add(char *object, MessageTag *mtags, char *line);
int hbn_history_request(Client *client, char *object, HistoryFilter *filter); HistoryResult *hbn_history_request(char *object, HistoryFilter *filter);
int hbn_history_destroy(char *object); int hbn_history_destroy(char *object);
MOD_INIT() MOD_INIT()
@ -30,7 +30,6 @@ MOD_INIT()
HistoryBackendInfo hbi; HistoryBackendInfo hbi;
MARK_AS_OFFICIAL_MODULE(modinfo); MARK_AS_OFFICIAL_MODULE(modinfo);
ModuleSetOptions(modinfo->handle, MOD_OPT_PERM, 1);
memset(&hbi, 0, sizeof(hbi)); memset(&hbi, 0, sizeof(hbi));
hbi.name = "mem"; hbi.name = "mem";
@ -59,9 +58,9 @@ int hbn_history_add(char *object, MessageTag *mtags, char *line)
return 1; return 1;
} }
int hbn_history_request(Client *client, char *object, HistoryFilter *filter) HistoryResult *hbn_history_request(char *object, HistoryFilter *filter)
{ {
return 0; return NULL;
} }
int hbn_history_set_limit(char *object, int max_lines, long max_time) int hbn_history_set_limit(char *object, int max_lines, long max_time)

View File

@ -190,16 +190,6 @@ static void ident_lookup_receive(int fd, int revents, void *userdata)
return; return;
} }
void skip_whitespace(char **p)
{
for (; **p == ' ' || **p == '\t'; *p = *p + 1);
}
void read_until(char **p, char *stopchars)
{
for (; **p && !strchr(stopchars, **p); *p = *p + 1);
}
static char *ident_lookup_parse(Client *client, char *buf) static char *ident_lookup_parse(Client *client, char *buf)
{ {
/* <port> , <port> : USERID : <OSTYPE>: <username> /* <port> , <port> : USERID : <OSTYPE>: <username>

View File

@ -166,25 +166,16 @@ CMD_FUNC(cmd_invite)
return; return;
} }
if (MyConnect(client)) if (MyUser(client))
{ {
if (target_limit_exceeded(client, target, target->name)) if (target_limit_exceeded(client, target, target->name))
return; return;
if (!ValidatePermissionsForPath("immune:invite-flood",client,NULL,NULL,NULL)) if (!ValidatePermissionsForPath("immune:invite-flood",client,NULL,NULL,NULL) &&
flood_limit_exceeded(client, FLD_INVITE))
{ {
if ((client->user->flood.invite_t + INVITE_PERIOD) <= timeofday) sendnumeric(client, RPL_TRYAGAIN, "INVITE");
{ return;
client->user->flood.invite_c = 0;
client->user->flood.invite_t = timeofday;
}
if (client->user->flood.invite_c <= INVITE_COUNT)
client->user->flood.invite_c++;
if (client->user->flood.invite_c > INVITE_COUNT)
{
sendnumeric(client, RPL_TRYAGAIN, "INVITE");
return;
}
} }
if (!override) if (!override)

View File

@ -23,10 +23,6 @@
#include "unrealircd.h" #include "unrealircd.h"
/* Default settings for set::anti-flood::join-flood block: */
#define JOINTHROTTLE_DEFAULT_COUNT 3
#define JOINTHROTTLE_DEFAULT_TIME 90
ModuleHeader MOD_HEADER ModuleHeader MOD_HEADER
= { = {
"jointhrottle", "jointhrottle",
@ -40,11 +36,6 @@ ModuleInfo *ModInfo = NULL;
ModDataInfo *jointhrottle_md; /* Module Data structure which we acquire */ ModDataInfo *jointhrottle_md; /* Module Data structure which we acquire */
struct {
unsigned short num;
unsigned short t;
} cfg;
typedef struct JoinFlood JoinFlood; typedef struct JoinFlood JoinFlood;
struct JoinFlood { struct JoinFlood {
@ -55,8 +46,6 @@ struct JoinFlood {
}; };
/* Forward declarations */ /* Forward declarations */
int jointhrottle_config_test(ConfigFile *, ConfigEntry *, int, int *);
int jointhrottle_config_run(ConfigFile *, ConfigEntry *, int);
void jointhrottle_md_free(ModData *m); void jointhrottle_md_free(ModData *m);
int jointhrottle_can_join(Client *client, Channel *channel, char *key, char *parv[]); int jointhrottle_can_join(Client *client, Channel *channel, char *key, char *parv[]);
int jointhrottle_local_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]); int jointhrottle_local_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]);
@ -67,7 +56,6 @@ JoinFlood *jointhrottle_addentry(Client *client, Channel *channel);
MOD_TEST() MOD_TEST()
{ {
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, jointhrottle_config_test);
return MOD_SUCCESS; return MOD_SUCCESS;
} }
@ -89,12 +77,9 @@ MOD_INIT()
if (!jointhrottle_md) if (!jointhrottle_md)
abort(); abort();
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, jointhrottle_config_run);
HookAdd(modinfo->handle, HOOKTYPE_CAN_JOIN, 0, jointhrottle_can_join); HookAdd(modinfo->handle, HOOKTYPE_CAN_JOIN, 0, jointhrottle_can_join);
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_JOIN, 0, jointhrottle_local_join); HookAdd(modinfo->handle, HOOKTYPE_LOCAL_JOIN, 0, jointhrottle_local_join);
cfg.t = JOINTHROTTLE_DEFAULT_TIME;
cfg.num = JOINTHROTTLE_DEFAULT_COUNT;
return MOD_SUCCESS; return MOD_SUCCESS;
} }
@ -109,53 +94,10 @@ MOD_UNLOAD()
return MOD_FAILED; return MOD_FAILED;
} }
int jointhrottle_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
{
int errors = 0;
int cnt=0, period=0;
if (type != CONFIG_SET_ANTI_FLOOD)
return 0;
if (strcmp(ce->ce_varname, "join-flood"))
return 0; /* otherwise not interested */
if (!ce->ce_vardata || !config_parse_flood(ce->ce_vardata, &cnt, &period) ||
(cnt < 1) || (cnt > 255) || (period < 5))
{
config_error("%s:%i: set::anti-flood::join-flood. Syntax is '<count>:<period>' (eg 3:90), "
"count should be 1-255, period should be greater than 4",
ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
errors++;
}
*errs = errors;
return errors ? -1 : 1;
}
int jointhrottle_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
{
int cnt=0, period=0;
if (type != CONFIG_SET_ANTI_FLOOD)
return 0;
if (strcmp(ce->ce_varname, "join-flood"))
return 0; /* otherwise not interested */
config_parse_flood(ce->ce_vardata, &cnt, &period);
cfg.t = period;
cfg.num = cnt;
return 0;
}
static int isjthrottled(Client *client, Channel *channel) static int isjthrottled(Client *client, Channel *channel)
{ {
JoinFlood *e; JoinFlood *e;
int num = cfg.num; FloodSettings *settings = get_floodsettings_for_user(client, FLD_JOIN);
int t = cfg.t;
if (!MyUser(client)) if (!MyUser(client))
return 0; return 0;
@ -171,7 +113,8 @@ static int isjthrottled(Client *client, Channel *channel)
/* Ok... now the actual check: /* Ok... now the actual check:
* if ([timer valid] && [one more join would exceed num]) * if ([timer valid] && [one more join would exceed num])
*/ */
if (((TStime() - e->firstjoin) < t) && (e->numjoins == num)) if (((TStime() - e->firstjoin) < settings->period[FLD_JOIN]) &&
(e->numjoins >= settings->limit[FLD_JOIN]))
return 1; /* Throttled */ return 1; /* Throttled */
return 0; return 0;
@ -196,7 +139,7 @@ static void jointhrottle_increase_usercounter(Client *client, Channel *channel)
e->firstjoin = TStime(); e->firstjoin = TStime();
e->numjoins = 1; e->numjoins = 1;
} else } else
if ((TStime() - e->firstjoin) < cfg.t) /* still valid? */ if ((TStime() - e->firstjoin) < iConf.floodsettings->period[FLD_JOIN]) /* still valid? */
{ {
e->numjoins++; e->numjoins++;
} else { } else {
@ -266,11 +209,12 @@ EVENT(jointhrottle_cleanup_structs)
{ {
jf_next = jf->next; jf_next = jf->next;
if (jf->firstjoin + cfg.t > TStime()) if (jf->firstjoin + iConf.floodsettings->period[FLD_JOIN] > TStime())
continue; /* still valid entry */ continue; /* still valid entry */
#ifdef DEBUGMODE #ifdef DEBUGMODE
ircd_log(LOG_ERROR, "jointhrottle_cleanup_structs(): freeing %s/%s (%ld[%ld], %d)", ircd_log(LOG_ERROR, "jointhrottle_cleanup_structs(): freeing %s/%s (%ld[%ld], %ld)",
client->name, jf->chname, jf->firstjoin, (long)(TStime() - jf->firstjoin), cfg.t); client->name, jf->chname, jf->firstjoin, (long)(TStime() - jf->firstjoin),
iConf.floodsettings->period[FLD_JOIN]);
#endif #endif
if (moddata_local_client(client, jointhrottle_md).ptr == jf) if (moddata_local_client(client, jointhrottle_md).ptr == jf)
{ {

View File

@ -132,21 +132,12 @@ CMD_FUNC(cmd_knock)
if (i == HOOK_DENY) if (i == HOOK_DENY)
return; return;
if (MyUser(client) && !ValidatePermissionsForPath("immune:knock-flood",client,NULL,NULL,NULL)) if (MyUser(client) &&
!ValidatePermissionsForPath("immune:knock-flood",client,NULL,NULL,NULL) &&
flood_limit_exceeded(client, FLD_KNOCK))
{ {
if ((client->user->flood.knock_t + KNOCK_PERIOD) <= timeofday) sendnumeric(client, ERR_CANNOTKNOCK, parv[1], "You are KNOCK flooding");
{ return;
client->user->flood.knock_c = 0;
client->user->flood.knock_t = timeofday;
}
if (client->user->flood.knock_c <= KNOCK_COUNT)
client->user->flood.knock_c++;
if (client->user->flood.knock_c > KNOCK_COUNT)
{
sendnumeric(client, ERR_CANNOTKNOCK, parv[1],
"You are KNOCK flooding");
return;
}
} }
new_message(&me, NULL, &mtags); new_message(&me, NULL, &mtags);

View File

@ -31,14 +31,6 @@ ModuleHeader MOD_HEADER
"unrealircd-5", "unrealircd-5",
}; };
/** The length of a standard 'msgid' tag (note that special
* msgid tags will be longer).
* The 22 alphanumeric characters provide slightly more
* than 128 bits of randomness (62^22 > 2^128).
* See mtag_add_or_inherit_msgid() for more information.
*/
#define MSGIDLEN 22
/* Variables */ /* Variables */
long CAP_ACCOUNT_TAG = 0L; long CAP_ACCOUNT_TAG = 0L;

View File

@ -117,8 +117,14 @@ int can_send_to_user(Client *client, Client *target, char **msgtext, char **errm
} }
// Possible FIXME: make match_spamfilter also use errmsg, or via a wrapper? or use same numeric? // Possible FIXME: make match_spamfilter also use errmsg, or via a wrapper? or use same numeric?
if (MyUser(client) && match_spamfilter(client, *msgtext, (sendtype == SEND_TYPE_NOTICE ? SPAMF_USERNOTICE : SPAMF_USERMSG), target->name, 0, NULL)) if (MyUser(client))
return 0; {
int spamtype = (sendtype == SEND_TYPE_NOTICE ? SPAMF_USERNOTICE : SPAMF_USERMSG);
char *cmd = sendtype_to_cmd(sendtype);
if (match_spamfilter(client, *msgtext, spamtype, cmd, target->name, 0, NULL))
return 0;
}
n = HOOK_CONTINUE; n = HOOK_CONTINUE;
for (h = Hooks[HOOKTYPE_CAN_SEND_TO_USER]; h; h = h->next) for (h = Hooks[HOOKTYPE_CAN_SEND_TO_USER]; h; h = h->next)
@ -379,8 +385,13 @@ void cmd_message(Client *client, MessageTag *recv_mtags, int parc, char *parv[],
if ((*parv[2] == '\001') && strncmp(&parv[2][1], "ACTION ", 7)) if ((*parv[2] == '\001') && strncmp(&parv[2][1], "ACTION ", 7))
sendflags |= SKIP_CTCP; sendflags |= SKIP_CTCP;
if (MyUser(client) && match_spamfilter(client, text, (sendtype == SEND_TYPE_NOTICE ? SPAMF_CHANNOTICE : SPAMF_CHANMSG), channel->chname, 0, NULL)) if (MyUser(client))
return; {
int spamtype = (sendtype == SEND_TYPE_NOTICE ? SPAMF_CHANNOTICE : SPAMF_CHANMSG);
if (match_spamfilter(client, text, spamtype, cmd, channel->chname, 0, NULL))
return;
}
new_message(client, recv_mtags, &mtags); new_message(client, recv_mtags, &mtags);

View File

@ -1293,7 +1293,7 @@ int do_extmode_char(Channel *channel, Cmode *handler, char *param, u_int what,
if (handler->is_ok(client, channel, mode, param, EXCHK_PARAM, what) == FALSE) if (handler->is_ok(client, channel, mode, param, EXCHK_PARAM, what) == FALSE)
return paracnt; /* rejected by is_ok */ return paracnt; /* rejected by is_ok */
morphed = handler->conv_param(param, client); morphed = handler->conv_param(param, client, channel);
if (!morphed) if (!morphed)
return paracnt; /* rejected by conv_param */ return paracnt; /* rejected by conv_param */
@ -1303,12 +1303,12 @@ int do_extmode_char(Channel *channel, Cmode *handler, char *param, u_int what,
char *now, *requested; char *now, *requested;
char flag = handler->flag; char flag = handler->flag;
now = cm_getparameter(channel, flag); now = cm_getparameter(channel, flag);
requested = handler->conv_param(param, client); requested = handler->conv_param(param, client, channel);
if (now && requested && !strcmp(now, requested)) if (now && requested && !strcmp(now, requested))
return paracnt; /* ignore... */ return paracnt; /* ignore... */
} }
ircsnprintf(pvar[*pcount], MODEBUFLEN + 3, "+%c%s", ircsnprintf(pvar[*pcount], MODEBUFLEN + 3, "+%c%s",
handler->flag, handler->conv_param(param, client)); handler->flag, handler->conv_param(param, client, channel));
(*pcount)++; (*pcount)++;
param = morphed; /* set param to converted parameter. */ param = morphed; /* set param to converted parameter. */
} }

View File

@ -214,11 +214,10 @@ CMD_FUNC(cmd_nick_remote)
sendto_snomask(SNO_FNICKCHANGE, "*** %s (%s@%s) has changed their nickname to %s", sendto_snomask(SNO_FNICKCHANGE, "*** %s (%s@%s) has changed their nickname to %s",
client->name, client->user->username, client->user->realhost, nick); client->name, client->user->username, client->user->realhost, nick);
RunHook2(HOOKTYPE_REMOTE_NICKCHANGE, client, nick); new_message(client, recv_mtags, &mtags);
RunHook3(HOOKTYPE_REMOTE_NICKCHANGE, client, mtags, nick);
client->lastnick = lastnick ? lastnick : TStime(); client->lastnick = lastnick ? lastnick : TStime();
add_history(client, 1); add_history(client, 1);
new_message(client, recv_mtags, &mtags);
sendto_server(client, 0, 0, mtags, ":%s NICK %s %lld", sendto_server(client, 0, 0, mtags, ":%s NICK %s %lld",
client->id, nick, (long long)client->lastnick); client->id, nick, (long long)client->lastnick);
sendto_local_common_channels(client, client, 0, mtags, ":%s NICK :%s", client->name, nick); sendto_local_common_channels(client, client, 0, mtags, ":%s NICK :%s", client->name, nick);
@ -275,19 +274,6 @@ CMD_FUNC(cmd_nick_local)
return; return;
} }
/* set::anti-flood::nick-flood */
if (client->user && !ValidatePermissionsForPath("immune:nick-flood",client,NULL,NULL,NULL))
{
if ((client->user->flood.nick_c >= NICK_COUNT) &&
(TStime() - client->user->flood.nick_t < NICK_PERIOD))
{
/* Throttle... */
sendnumeric(client, ERR_NCHANGETOOFAST, nick,
(int)(NICK_PERIOD - (TStime() - client->user->flood.nick_t)));
return;
}
}
/* Check for collisions / in use */ /* Check for collisions / in use */
if (!strcasecmp("ircd", nick) || !strcasecmp("irc", nick)) if (!strcasecmp("ircd", nick) || !strcasecmp("irc", nick))
{ {
@ -299,7 +285,7 @@ CMD_FUNC(cmd_nick_local)
{ {
/* Local client changing nick: check spamfilter */ /* Local client changing nick: check spamfilter */
spamfilter_build_user_string(spamfilter_user, nick, client); spamfilter_build_user_string(spamfilter_user, nick, client);
if (match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, 0, NULL)) if (match_spamfilter(client, spamfilter_user, SPAMF_USER, "NICK", NULL, 0, NULL))
return; return;
} }
@ -322,6 +308,16 @@ CMD_FUNC(cmd_nick_local)
/* fallthrough for ircops that have sufficient privileges */ /* fallthrough for ircops that have sufficient privileges */
} }
/* set::anti-flood::nick-flood */
if (client->user &&
!ValidatePermissionsForPath("immune:nick-flood",client,NULL,NULL,NULL) &&
flood_limit_exceeded(client, FLD_NICK))
{
/* Throttle... */
sendnumeric(client, ERR_NCHANGETOOFAST, nick);
return;
}
if (!ValidatePermissionsForPath("immune:nick-flood",client,NULL,NULL,NULL)) if (!ValidatePermissionsForPath("immune:nick-flood",client,NULL,NULL,NULL))
cptr->local->since += 3; /* Nick-flood prot. -Donwulff */ cptr->local->since += 3; /* Nick-flood prot. -Donwulff */
@ -440,20 +436,13 @@ CMD_FUNC(cmd_nick_local)
} }
} }
if (TStime() - client->user->flood.nick_t >= NICK_PERIOD)
{
client->user->flood.nick_t = TStime();
client->user->flood.nick_c = 1;
} else
client->user->flood.nick_c++;
sendto_snomask(SNO_NICKCHANGE, "*** %s (%s@%s) has changed their nickname to %s", sendto_snomask(SNO_NICKCHANGE, "*** %s (%s@%s) has changed their nickname to %s",
client->name, client->user->username, client->user->realhost, nick); client->name, client->user->username, client->user->realhost, nick);
RunHook2(HOOKTYPE_LOCAL_NICKCHANGE, client, nick); new_message(client, recv_mtags, &mtags);
RunHook3(HOOKTYPE_LOCAL_NICKCHANGE, client, mtags, nick);
client->lastnick = TStime(); client->lastnick = TStime();
add_history(client, 1); add_history(client, 1);
new_message(client, recv_mtags, &mtags);
sendto_server(client, 0, 0, mtags, ":%s NICK %s %lld", sendto_server(client, 0, 0, mtags, ":%s NICK %s %lld",
client->id, nick, (long long)client->lastnick); client->id, nick, (long long)client->lastnick);
sendto_local_common_channels(client, client, 0, mtags, ":%s NICK :%s", client->name, nick); sendto_local_common_channels(client, client, 0, mtags, ":%s NICK :%s", client->name, nick);
@ -681,7 +670,12 @@ nickkill2done:
return; return;
if (client->user->svid[0] != '0') if (client->user->svid[0] != '0')
{
user_account_login(recv_mtags, client); user_account_login(recv_mtags, client);
/* no need to check for kill upon user_account_login() here
* since that can only happen for local users.
*/
}
RunHook(HOOKTYPE_REMOTE_CONNECT, client); RunHook(HOOKTYPE_REMOTE_CONNECT, client);
@ -721,7 +715,7 @@ int _register_user(Client *client, char *nick, char *username, char *umode, char
userbad[USERLEN * 2 + 1], *ubad = userbad, noident = 0; userbad[USERLEN * 2 + 1], *ubad = userbad, noident = 0;
int i, xx; int i, xx;
Hook *h; Hook *h;
ClientUser *user = client->user; User *user = client->user;
char *tkllayer[9] = { char *tkllayer[9] = {
me.name, /*0 server.name */ me.name, /*0 server.name */
"+", /*1 +|- */ "+", /*1 +|- */
@ -879,7 +873,7 @@ int _register_user(Client *client, char *nick, char *username, char *umode, char
find_shun(client); find_shun(client);
spamfilter_build_user_string(spamfilter_user, client->name, client); spamfilter_build_user_string(spamfilter_user, client->name, client);
if (match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, 0, &savetkl)) if (match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, NULL, 0, &savetkl))
{ {
if (savetkl && ((savetkl->ptr.spamfilter->action == BAN_ACT_VIRUSCHAN) || if (savetkl && ((savetkl->ptr.spamfilter->action == BAN_ACT_VIRUSCHAN) ||
(savetkl->ptr.spamfilter->action == BAN_ACT_SOFT_VIRUSCHAN))) (savetkl->ptr.spamfilter->action == BAN_ACT_SOFT_VIRUSCHAN)))

View File

@ -89,7 +89,7 @@ CMD_FUNC(cmd_part)
} }
if (commentx) if (commentx)
{ {
if (match_spamfilter(client, commentx, SPAMF_PART, parv[1], 0, NULL)) if (match_spamfilter(client, commentx, SPAMF_PART, "PART", parv[1], 0, NULL))
commentx = NULL; commentx = NULL;
if (IsDead(client)) if (IsDead(client))
return; return;

View File

@ -82,7 +82,7 @@ CMD_FUNC(cmd_quit)
return; return;
} }
if (match_spamfilter(client, comment, SPAMF_QUIT, NULL, 0, NULL)) if (match_spamfilter(client, comment, SPAMF_QUIT, "QUIT", NULL, 0, NULL))
{ {
comment = client->name; comment = client->name;
if (IsDead(client)) if (IsDead(client))

116
src/modules/reply-tag.c Normal file
View File

@ -0,0 +1,116 @@
/*
* IRC - Internet Relay Chat, src/modules/reply-tag.c
* (C) 2021 Syzop & The UnrealIRCd Team
*
* See file AUTHORS in IRC package for additional names of
* the programmers.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* This implements https://ircv3.net/specs/client-tags/reply */
#include "unrealircd.h"
ModuleHeader MOD_HEADER
= {
"reply-tag",
"5.0",
"+reply client tag",
"UnrealIRCd Team",
"unrealircd-5",
};
int replytag_mtag_is_ok(Client *client, char *name, char *value);
void mtag_add_replytag(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature);
MOD_INIT()
{
MessageTagHandlerInfo mtag;
MARK_AS_OFFICIAL_MODULE(modinfo);
#if 0
memset(&mtag, 0, sizeof(mtag));
mtag.name = "+reply";
mtag.is_ok = replytag_mtag_is_ok;
mtag.flags = MTAG_HANDLER_FLAGS_NO_CAP_NEEDED;
MessageTagHandlerAdd(modinfo->handle, &mtag);
#endif
memset(&mtag, 0, sizeof(mtag));
mtag.name = "+draft/reply";
mtag.is_ok = replytag_mtag_is_ok;
mtag.flags = MTAG_HANDLER_FLAGS_NO_CAP_NEEDED;
MessageTagHandlerAdd(modinfo->handle, &mtag);
HookAddVoid(modinfo->handle, HOOKTYPE_NEW_MESSAGE, 0, mtag_add_replytag);
return MOD_SUCCESS;
}
MOD_LOAD()
{
return MOD_SUCCESS;
}
MOD_UNLOAD()
{
return MOD_SUCCESS;
}
/** This function verifies if the client sending the mtag is permitted to do so.
*/
int replytag_mtag_is_ok(Client *client, char *name, char *value)
{
char *p;
/* Require a non-empty parameter */
if (BadPtr(value))
return 0;
/* All our PRIVMSG/NOTICE msgid's are of this size: */
if (strlen(value) != MSGIDLEN)
return 0;
for (p = value; *p; p++)
if (!isalnum(*p))
return 0; /* non-alphanumeric */
return 1; /* OK */
}
void mtag_add_replytag(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature)
{
MessageTag *m;
if (IsUser(client))
{
#if 0
m = find_mtag(recv_mtags, "+reply");
if (m)
{
m = duplicate_mtag(m);
AddListItem(m, *mtag_list);
}
#endif
m = find_mtag(recv_mtags, "+draft/reply");
if (m)
{
m = duplicate_mtag(m);
AddListItem(m, *mtag_list);
}
}
}

View File

@ -19,9 +19,12 @@
#define REPUTATION_VERSION "1.2" #define REPUTATION_VERSION "1.2"
/* Change to #define to benchmark. Note that this will add random
* reputation entries so should never be used on production servers!!!
*/
#undef BENCHMARK
#undef TEST #undef TEST
#undef BENCHMARK
/* Benchmark results (2GHz Xeon Skylake, compiled with -O2, Linux): /* Benchmark results (2GHz Xeon Skylake, compiled with -O2, Linux):
* 10k random IP's with various expire times: * 10k random IP's with various expire times:
* - load db: 23 ms * - load db: 23 ms
@ -76,12 +79,30 @@ ModuleHeader MOD_HEADER
#define Reputation(client) moddata_client(client, reputation_md).l #define Reputation(client) moddata_client(client, reputation_md).l
#define WARN_WRITE_ERROR(fname) \
do { \
sendto_realops_and_log("[reputation] Error writing to temporary database file " \
"'%s': %s (DATABASE NOT SAVED)", \
fname, unrealdb_get_error_string()); \
} while(0)
#define W_SAFE(x) \
do { \
if (!(x)) { \
WARN_WRITE_ERROR(tmpfname); \
unrealdb_close(db); \
return 0; \
} \
} while(0)
/* Definitions (structs, etc.) */ /* Definitions (structs, etc.) */
struct cfgstruct { struct cfgstruct {
int expire_score[MAXEXPIRES]; int expire_score[MAXEXPIRES];
long expire_time[MAXEXPIRES]; long expire_time[MAXEXPIRES];
char *database; char *database;
char *db_secret;
}; };
typedef struct ReputationEntry ReputationEntry; typedef struct ReputationEntry ReputationEntry;
@ -97,6 +118,7 @@ struct ReputationEntry {
/* Global variables */ /* Global variables */
static struct cfgstruct cfg; /**< Current configuration */ static struct cfgstruct cfg; /**< Current configuration */
static struct cfgstruct test; /**< Testing configuration (not active yet) */
long reputation_starttime = 0; long reputation_starttime = 0;
long reputation_writtentime = 0; long reputation_writtentime = 0;
@ -111,7 +133,8 @@ ModDataInfo *reputation_md; /* Module Data structure which we acquire */
void reputation_md_free(ModData *m); void reputation_md_free(ModData *m);
char *reputation_md_serialize(ModData *m); char *reputation_md_serialize(ModData *m);
void reputation_md_unserialize(char *str, ModData *m); void reputation_md_unserialize(char *str, ModData *m);
void config_setdefaults(void); void reputation_config_setdefaults(struct cfgstruct *cfg);
void reputation_free_config(struct cfgstruct *cfg);
CMD_FUNC(reputation_cmd); CMD_FUNC(reputation_cmd);
CMD_FUNC(reputationunperm); CMD_FUNC(reputationunperm);
int reputation_whois(Client *client, Client *target); int reputation_whois(Client *client, Client *target);
@ -122,18 +145,21 @@ int reputation_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
int reputation_config_run(ConfigFile *cf, ConfigEntry *ce, int type); int reputation_config_run(ConfigFile *cf, ConfigEntry *ce, int type);
int reputation_config_posttest(int *errs); int reputation_config_posttest(int *errs);
static uint64_t hash_reputation_entry(char *ip); static uint64_t hash_reputation_entry(char *ip);
ReputationEntry *find_reputation_entry(char *ip);
void add_reputation_entry(ReputationEntry *e); void add_reputation_entry(ReputationEntry *e);
EVENT(delete_old_records); EVENT(delete_old_records);
EVENT(add_scores); EVENT(add_scores);
EVENT(save_db_evt); EVENT(reputation_save_db_evt);
void load_db(void); int reputation_load_db(void);
void save_db(void); int reputation_save_db(void);
int reputation_starttime_callback(void); int reputation_starttime_callback(void);
MOD_TEST() MOD_TEST()
{ {
memcpy(&ModInf, modinfo, modinfo->size); memcpy(&ModInf, modinfo, modinfo->size);
memset(&cfg, 0, sizeof(cfg)); memset(&cfg, 0, sizeof(cfg));
memset(&test, 0, sizeof(cfg));
reputation_config_setdefaults(&test);
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, reputation_config_test); HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, reputation_config_test);
HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, reputation_config_posttest); HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, reputation_config_posttest);
CallbackAddEx(modinfo->handle, CALLBACKTYPE_REPUTATION_STARTTIME, reputation_starttime_callback); CallbackAddEx(modinfo->handle, CALLBACKTYPE_REPUTATION_STARTTIME, reputation_starttime_callback);
@ -146,6 +172,7 @@ MOD_INIT()
MARK_AS_OFFICIAL_MODULE(modinfo); MARK_AS_OFFICIAL_MODULE(modinfo);
ModuleSetOptions(modinfo->handle, MOD_OPT_PERM, 1); ModuleSetOptions(modinfo->handle, MOD_OPT_PERM, 1);
memset(&ReputationHashTable, 0, sizeof(ReputationHashTable)); memset(&ReputationHashTable, 0, sizeof(ReputationHashTable));
siphash_generate_key(siphashkey_reputation); siphash_generate_key(siphashkey_reputation);
@ -160,7 +187,7 @@ MOD_INIT()
if (!reputation_md) if (!reputation_md)
abort(); abort();
config_setdefaults(); reputation_config_setdefaults(&cfg);
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, reputation_config_run); HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, reputation_config_run);
HookAdd(modinfo->handle, HOOKTYPE_WHOIS, 0, reputation_whois); HookAdd(modinfo->handle, HOOKTYPE_WHOIS, 0, reputation_whois);
HookAdd(modinfo->handle, HOOKTYPE_HANDSHAKE, 0, reputation_set_on_connect); HookAdd(modinfo->handle, HOOKTYPE_HANDSHAKE, 0, reputation_set_on_connect);
@ -172,43 +199,79 @@ MOD_INIT()
return MOD_SUCCESS; return MOD_SUCCESS;
} }
#ifdef BENCHMARK
void reputation_benchmark(int entries)
{
char ip[64];
int i;
ReputationEntry *e;
srand(1234); // fixed seed
for (i = 0; i < entries; i++)
{
ReputationEntry *e = safe_alloc(sizeof(ReputationEntry) + 64);
snprintf(e->ip, 63, "%d.%d.%d.%d", rand()%255, rand()%255, rand()%255, rand()%255);
e->score = rand()%255 + 1;
e->last_seen = TStime();
if (find_reputation_entry(e->ip))
{
safe_free(e);
continue;
}
add_reputation_entry(e);
}
}
#endif
MOD_LOAD() MOD_LOAD()
{ {
load_db(); reputation_load_db();
if (reputation_starttime == 0) if (reputation_starttime == 0)
reputation_starttime = TStime(); reputation_starttime = TStime();
EventAdd(ModInf.handle, "delete_old_records", delete_old_records, NULL, DELETE_OLD_EVERY*1000, 0); EventAdd(ModInf.handle, "delete_old_records", delete_old_records, NULL, DELETE_OLD_EVERY*1000, 0);
EventAdd(ModInf.handle, "add_scores", add_scores, NULL, BUMP_SCORE_EVERY*1000, 0); EventAdd(ModInf.handle, "add_scores", add_scores, NULL, BUMP_SCORE_EVERY*1000, 0);
EventAdd(ModInf.handle, "save_db", save_db_evt, NULL, SAVE_DB_EVERY*1000, 0); EventAdd(ModInf.handle, "reputation_save_db", reputation_save_db_evt, NULL, SAVE_DB_EVERY*1000, 0);
#ifdef BENCHMARK
reputation_benchmark(10000);
#endif
return MOD_SUCCESS; return MOD_SUCCESS;
} }
MOD_UNLOAD() MOD_UNLOAD()
{ {
save_db(); if (loop.ircd_terminating)
reputation_save_db();
reputation_free_config(&test);
reputation_free_config(&cfg);
return MOD_SUCCESS; return MOD_SUCCESS;
} }
void config_setdefaults(void) void reputation_config_setdefaults(struct cfgstruct *cfg)
{ {
/* data/reputation.db */ /* data/reputation.db */
safe_strdup(cfg.database, "reputation.db"); safe_strdup(cfg->database, "reputation.db");
convert_to_absolute_path(&cfg.database, PERMDATADIR); convert_to_absolute_path(&cfg->database, PERMDATADIR);
/* EXPIRES the following entries if the IP does appear for some time: */ /* EXPIRES the following entries if the IP does appear for some time: */
/* <=2 points after 1 hour */ /* <=2 points after 1 hour */
cfg.expire_score[0] = 2; cfg->expire_score[0] = 2;
#ifndef TEST #ifndef TEST
cfg.expire_time[0] = 3600; cfg->expire_time[0] = 3600;
#else #else
cfg.expire_time[0] = 36; cfg->expire_time[0] = 36;
#endif #endif
/* <=6 points after 7 days */ /* <=6 points after 7 days */
cfg.expire_score[1] = 6; cfg->expire_score[1] = 6;
cfg.expire_time[1] = 86400*7; cfg->expire_time[1] = 86400*7;
/* ANY result that has not been seen for 30 days */ /* ANY result that has not been seen for 30 days */
cfg.expire_score[2] = -1; cfg->expire_score[2] = -1;
cfg.expire_time[2] = 86400*30; cfg->expire_time[2] = 86400*30;
}
void reputation_free_config(struct cfgstruct *cfg)
{
safe_free(cfg->database);
safe_free(cfg->db_secret);
} }
int reputation_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) int reputation_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
@ -218,11 +281,11 @@ int reputation_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
if (type != CONFIG_SET) if (type != CONFIG_SET)
return 0; return 0;
/* We are only interrested in set::reputation.. */ /* We are only interrested in set::reputation.. */
if (!ce || strcmp(ce->ce_varname, "reputation")) if (!ce || strcmp(ce->ce_varname, "reputation"))
return 0; return 0;
for (cep = ce->ce_entries; cep; cep = cep->ce_next) for (cep = ce->ce_entries; cep; cep = cep->ce_next)
{ {
if (!cep->ce_vardata) if (!cep->ce_vardata)
@ -235,6 +298,18 @@ int reputation_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
if (!strcmp(cep->ce_varname, "database")) if (!strcmp(cep->ce_varname, "database"))
{ {
convert_to_absolute_path(&cep->ce_vardata, PERMDATADIR); convert_to_absolute_path(&cep->ce_vardata, PERMDATADIR);
safe_strdup(test.database, cep->ce_vardata);
} else
if (!strcmp(cep->ce_varname, "db-secret"))
{
char *err;
if ((err = unrealdb_test_secret(cep->ce_vardata)))
{
config_error("%s:%i: set::channeldb::db-secret: %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, err);
errors++;
continue;
}
safe_strdup(test.db_secret, cep->ce_vardata);
} else } else
{ {
config_error("%s:%i: unknown directive set::reputation::%s", config_error("%s:%i: unknown directive set::reputation::%s",
@ -243,7 +318,7 @@ int reputation_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
continue; continue;
} }
} }
*errs = errors; *errs = errors;
return errors ? -1 : 1; return errors ? -1 : 1;
} }
@ -254,16 +329,20 @@ int reputation_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
if (type != CONFIG_SET) if (type != CONFIG_SET)
return 0; return 0;
/* We are only interrested in set::reputation.. */ /* We are only interrested in set::reputation.. */
if (!ce || strcmp(ce->ce_varname, "reputation")) if (!ce || strcmp(ce->ce_varname, "reputation"))
return 0; return 0;
for (cep = ce->ce_entries; cep; cep = cep->ce_next) for (cep = ce->ce_entries; cep; cep = cep->ce_next)
{ {
if (!strcmp(cep->ce_varname, "database")) if (!strcmp(cep->ce_varname, "database"))
{ {
safe_strdup(cfg.database, cep->ce_vardata); safe_strdup(cfg.database, cep->ce_vardata);
} else
if (!strcmp(cep->ce_varname, "db-secret"))
{
safe_strdup(cfg.db_secret, cep->ce_vardata);
} }
} }
return 1; return 1;
@ -272,13 +351,20 @@ int reputation_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
int reputation_config_posttest(int *errs) int reputation_config_posttest(int *errs)
{ {
int errors = 0; int errors = 0;
char *errstr;
if (test.database && ((errstr = unrealdb_test_db(test.database, test.db_secret))))
{
config_error("[reputation] %s", errstr);
errors++;
}
*errs = errors; *errs = errors;
return errors ? -1 : 1; return errors ? -1 : 1;
} }
/** Parse database header and set variables appropriately */ /** Parse database header and set variables appropriately */
int parse_db_header(char *buf) int parse_db_header_old(char *buf)
{ {
char *header=NULL, *version=NULL, *starttime=NULL, *writtentime=NULL; char *header=NULL, *version=NULL, *starttime=NULL, *writtentime=NULL;
char *p=NULL; char *p=NULL;
@ -308,7 +394,7 @@ int parse_db_header(char *buf)
return 1; return 1;
} }
void load_db(void) void reputation_load_db_old(void)
{ {
FILE *fd; FILE *fd;
char buf[512], *p; char buf[512], *p;
@ -324,7 +410,7 @@ void load_db(void)
config_warn("WARNING: Could not open/read database '%s': %s", cfg.database, strerror(ERRNO)); config_warn("WARNING: Could not open/read database '%s': %s", cfg.database, strerror(ERRNO));
return; return;
} }
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
if (fgets(buf, 512, fd) == NULL) if (fgets(buf, 512, fd) == NULL)
{ {
@ -332,7 +418,7 @@ void load_db(void)
fclose(fd); fclose(fd);
return; return;
} }
/* Header contains: REPDB <version> <starttime> <writtentime> /* Header contains: REPDB <version> <starttime> <writtentime>
* Where: * Where:
* REPDB: Literally the string "REPDB". * REPDB: Literally the string "REPDB".
@ -341,7 +427,7 @@ void load_db(void)
* in other words: when this module was first loaded, ever. * in other words: when this module was first loaded, ever.
* <writtentime> Time that the database was last written. * <writtentime> Time that the database was last written.
*/ */
if (!parse_db_header(buf)) if (!parse_db_header_old(buf))
{ {
config_error("WARNING: Cannot load database %s. Error reading header. " config_error("WARNING: Cannot load database %s. Error reading header. "
"Database corrupt? Or are you downgrading from a newer " "Database corrupt? Or are you downgrading from a newer "
@ -355,7 +441,7 @@ void load_db(void)
{ {
char *ip = NULL, *score = NULL, *last_seen = NULL; char *ip = NULL, *score = NULL, *last_seen = NULL;
ReputationEntry *e; ReputationEntry *e;
stripcrlf(buf); stripcrlf(buf);
/* Format: <ip> <score> <last seen> */ /* Format: <ip> <score> <last seen> */
ip = strtoken(&p, buf, " "); ip = strtoken(&p, buf, " ");
@ -367,12 +453,12 @@ void load_db(void)
last_seen = strtoken(&p, NULL, " "); last_seen = strtoken(&p, NULL, " ");
if (!last_seen) if (!last_seen)
continue; continue;
e = safe_alloc(sizeof(ReputationEntry)+strlen(ip)); e = safe_alloc(sizeof(ReputationEntry)+strlen(ip));
strcpy(e->ip, ip); /* safe, see alloc above */ strcpy(e->ip, ip); /* safe, see alloc above */
e->score = atoi(score); e->score = atoi(score);
e->last_seen = atol(last_seen); e->last_seen = atol(last_seen);
add_reputation_entry(e); add_reputation_entry(e);
} }
fclose(fd); fclose(fd);
@ -384,7 +470,130 @@ void load_db(void)
#endif #endif
} }
void save_db(void) #define R_SAFE(x) \
do { \
if (!(x)) { \
config_warn("[reputation] Read error from database file '%s' (possible corruption): %s", cfg.database, unrealdb_get_error_string()); \
unrealdb_close(db); \
safe_free(ip); \
return 0; \
} \
} while(0)
int reputation_load_db_new(UnrealDB *db)
{
uint64_t l_db_version = 0;
uint64_t l_starttime = 0;
uint64_t l_writtentime = 0;
uint64_t count = 0;
uint64_t i;
char *ip = NULL;
uint16_t score;
uint64_t last_seen;
ReputationEntry *e;
#ifdef BENCHMARK
struct timeval tv_alpha, tv_beta;
gettimeofday(&tv_alpha, NULL);
#endif
R_SAFE(unrealdb_read_int64(db, &l_db_version)); /* reputation db version */
if (l_db_version > 2)
{
config_error("[reputation] Reputation DB is of a newer version (%ld) than supported by us (%ld). "
"Did you perhaps downgrade your UnrealIRCd?",
(long)l_db_version, (long)2);
unrealdb_close(db);
return 0;
}
R_SAFE(unrealdb_read_int64(db, &l_starttime)); /* starttime of data gathering */
R_SAFE(unrealdb_read_int64(db, &l_writtentime)); /* current time */
R_SAFE(unrealdb_read_int64(db, &count)); /* number of entries */
reputation_starttime = l_starttime;
reputation_writtentime = l_writtentime;
for (i=0; i < count; i++)
{
R_SAFE(unrealdb_read_str(db, &ip));
R_SAFE(unrealdb_read_int16(db, &score));
R_SAFE(unrealdb_read_int64(db, &last_seen));
e = safe_alloc(sizeof(ReputationEntry)+strlen(ip));
strcpy(e->ip, ip); /* safe, see alloc above */
e->score = score;
e->last_seen = last_seen;
add_reputation_entry(e);
safe_free(ip);
}
unrealdb_close(db);
#ifdef BENCHMARK
gettimeofday(&tv_beta, NULL);
ircd_log(LOG_ERROR, "Reputation benchmark: LOAD DB: %lld microseconds",
(long long)(((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec)));
#endif
return 1;
}
/** Load the reputation DB.
* Strategy is:
* 1) Check for the old header "REPDB 1", if so then call reputation_load_db_old().
* 2) Otherwise, open with unrealdb routine
* 3) If that fails due to a password provided but the file is unrealdb without password
* then fallback to open without a password (so users can easily upgrade to encrypted)
*/
int reputation_load_db(void)
{
FILE *fd;
UnrealDB *db;
char buf[512];
fd = fopen(cfg.database, "r");
if (!fd)
{
/* Database does not exist. Could be first boot */
config_warn("[reputation] No database present at '%s', will start a new one", cfg.database);
return 1;
}
*buf = '\0';
if (fgets(buf, sizeof(buf), fd) == NULL)
{
fclose(fd);
config_warn("[reputation] Database at '%s' is 0 bytes", cfg.database);
return 1;
}
fclose(fd);
if (!strncmp(buf, "REPDB 1 ", 8))
{
reputation_load_db_old();
return 1; /* not so good to always pretend succes */
}
/* Otherwise, it is an unrealdb, crypted or not */
db = unrealdb_open(cfg.database, UNREALDB_MODE_READ, cfg.db_secret);
if (!db)
{
if (unrealdb_get_error_code() == UNREALDB_ERROR_FILENOTFOUND)
{
/* Database does not exist. Could be first boot */
config_warn("[reputation] No database present at '%s', will start a new one", cfg.database);
return 1;
} else
if (unrealdb_get_error_code() == UNREALDB_ERROR_NOTCRYPTED)
{
db = unrealdb_open(cfg.database, UNREALDB_MODE_READ, NULL);
}
if (!db)
{
config_error("[reputation] Unable to open the database file '%s' for reading: %s", cfg.database, unrealdb_get_error_string());
return 0;
}
}
return reputation_load_db_new(db);
}
int reputation_save_db_old(void)
{ {
FILE *fd; FILE *fd;
char tmpfname[512]; char tmpfname[512];
@ -395,19 +604,15 @@ void save_db(void)
gettimeofday(&tv_alpha, NULL); gettimeofday(&tv_alpha, NULL);
#endif #endif
#ifdef TEST
sendto_realops("REPUTATION IS RUNNING IN TEST MODE. SAVING DB'S...");
#endif
/* We write to a temporary file. Only to rename it later if everything was ok */ /* We write to a temporary file. Only to rename it later if everything was ok */
snprintf(tmpfname, sizeof(tmpfname), "%s.%x.tmp", cfg.database, getrandom32()); snprintf(tmpfname, sizeof(tmpfname), "%s.%x.tmp", cfg.database, getrandom32());
fd = fopen(tmpfname, "w"); fd = fopen(tmpfname, "w");
if (!fd) if (!fd)
{ {
config_error("ERROR: Could not open/write database '%s': %s -- DATABASE *NOT* SAVED!!!", tmpfname, strerror(ERRNO)); config_error("ERROR: Could not open/write database '%s': %s -- DATABASE *NOT* SAVED!!!", tmpfname, strerror(ERRNO));
return; return 0;
} }
if (fprintf(fd, "REPDB 1 %lld %lld\n", (long long)reputation_starttime, (long long)TStime()) < 0) if (fprintf(fd, "REPDB 1 %lld %lld\n", (long long)reputation_starttime, (long long)TStime()) < 0)
@ -422,7 +627,7 @@ void save_db(void)
write_fail: write_fail:
config_error("ERROR writing to '%s': %s -- DATABASE *NOT* SAVED!!!", tmpfname, strerror(ERRNO)); config_error("ERROR writing to '%s': %s -- DATABASE *NOT* SAVED!!!", tmpfname, strerror(ERRNO));
fclose(fd); fclose(fd);
return; return 0;
} }
} }
} }
@ -430,9 +635,9 @@ write_fail:
if (fclose(fd) < 0) if (fclose(fd) < 0)
{ {
config_error("ERROR writing to '%s': %s -- DATABASE *NOT* SAVED!!!", tmpfname, strerror(ERRNO)); config_error("ERROR writing to '%s': %s -- DATABASE *NOT* SAVED!!!", tmpfname, strerror(ERRNO));
return; return 0;
} }
/* Everything went fine. We rename our temporary file to the existing /* Everything went fine. We rename our temporary file to the existing
* DB file (will overwrite), which is more or less an atomic operation. * DB file (will overwrite), which is more or less an atomic operation.
*/ */
@ -444,7 +649,7 @@ write_fail:
{ {
config_error("ERROR renaming '%s' to '%s': %s -- DATABASE *NOT* SAVED!!!", config_error("ERROR renaming '%s' to '%s': %s -- DATABASE *NOT* SAVED!!!",
tmpfname, cfg.database, strerror(ERRNO)); tmpfname, cfg.database, strerror(ERRNO));
return; return 0;
} }
reputation_writtentime = TStime(); reputation_writtentime = TStime();
@ -455,7 +660,91 @@ write_fail:
(long long)(((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec))); (long long)(((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec)));
#endif #endif
return; return 1;
}
int reputation_save_db(void)
{
UnrealDB *db;
char tmpfname[512];
int i;
uint64_t count;
ReputationEntry *e;
#ifdef BENCHMARK
struct timeval tv_alpha, tv_beta;
gettimeofday(&tv_alpha, NULL);
#endif
#ifdef TEST
sendto_realops("REPUTATION IS RUNNING IN TEST MODE. SAVING DB'S...");
#endif
/* Comment this out after one or more releases (means you cannot downgrade to <=5.0.9.1 anymore) */
if (cfg.db_secret == NULL)
return reputation_save_db_old();
/* We write to a temporary file. Only to rename it later if everything was ok */
snprintf(tmpfname, sizeof(tmpfname), "%s.%x.tmp", cfg.database, getrandom32());
db = unrealdb_open(tmpfname, UNREALDB_MODE_WRITE, cfg.db_secret);
if (!db)
{
WARN_WRITE_ERROR(tmpfname);
return 0;
}
/* Write header */
W_SAFE(unrealdb_write_int64(db, 2)); /* reputation db version */
W_SAFE(unrealdb_write_int64(db, reputation_starttime)); /* starttime of data gathering */
W_SAFE(unrealdb_write_int64(db, TStime())); /* current time */
/* Count entries */
count = 0;
for (i = 0; i < REPUTATION_HASH_TABLE_SIZE; i++)
for (e = ReputationHashTable[i]; e; e = e->next)
count++;
W_SAFE(unrealdb_write_int64(db, count)); /* Number of DB entries */
/* Now write the actual individual entries: */
for (i = 0; i < REPUTATION_HASH_TABLE_SIZE; i++)
{
for (e = ReputationHashTable[i]; e; e = e->next)
{
W_SAFE(unrealdb_write_str(db, e->ip));
W_SAFE(unrealdb_write_int16(db, e->score));
W_SAFE(unrealdb_write_int64(db, e->last_seen));
}
}
if (!unrealdb_close(db))
{
WARN_WRITE_ERROR(tmpfname);
return 0;
}
/* Everything went fine. We rename our temporary file to the existing
* DB file (will overwrite), which is more or less an atomic operation.
*/
#ifdef _WIN32
/* The rename operation cannot be atomic on Windows as it will cause a "file exists" error */
unlink(cfg.database);
#endif
if (rename(tmpfname, cfg.database) < 0)
{
config_error("ERROR renaming '%s' to '%s': %s -- DATABASE *NOT* SAVED!!!",
tmpfname, cfg.database, strerror(ERRNO));
return 0;
}
reputation_writtentime = TStime();
#ifdef BENCHMARK
gettimeofday(&tv_beta, NULL);
ircd_log(LOG_ERROR, "Reputation benchmark: SAVE DB: %lld microseconds",
(long long)(((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec)));
#endif
return 1;
} }
static uint64_t hash_reputation_entry(char *ip) static uint64_t hash_reputation_entry(char *ip)
@ -548,7 +837,7 @@ EVENT(add_scores)
*/ */
#define MARKER_UNREGISTERED_USER (marker) #define MARKER_UNREGISTERED_USER (marker)
#define MARKER_REGISTERED_USER (marker+1) #define MARKER_REGISTERED_USER (marker+1)
list_for_each_entry(client, &client_list, client_node) list_for_each_entry(client, &client_list, client_node)
{ {
if (!IsUser(client)) if (!IsUser(client))
@ -625,13 +914,13 @@ EVENT(delete_old_records)
gettimeofday(&tv_alpha, NULL); gettimeofday(&tv_alpha, NULL);
#endif #endif
for (i = 0; i < REPUTATION_HASH_TABLE_SIZE; i++) for (i = 0; i < REPUTATION_HASH_TABLE_SIZE; i++)
{ {
for (e = ReputationHashTable[i]; e; e = e_next) for (e = ReputationHashTable[i]; e; e = e_next)
{ {
e_next = e->next; e_next = e->next;
if (is_reputation_expired(e)) if (is_reputation_expired(e))
{ {
#ifdef DEBUGMODE #ifdef DEBUGMODE
@ -651,9 +940,9 @@ EVENT(delete_old_records)
#endif #endif
} }
EVENT(save_db_evt) EVENT(reputation_save_db_evt)
{ {
save_db(); reputation_save_db();
} }
CMD_FUNC(reputationunperm) CMD_FUNC(reputationunperm)
@ -801,7 +1090,7 @@ CMD_FUNC(reputation_user_cmd)
sendnumeric(client, ERR_NOPRIVILEGES); sendnumeric(client, ERR_NOPRIVILEGES);
return; return;
} }
if ((parc < 2) || BadPtr(parv[1])) if ((parc < 2) || BadPtr(parv[1]))
{ {
sendnotice(client, "Reputation module statistics:"); sendnotice(client, "Reputation module statistics:");
@ -825,7 +1114,7 @@ CMD_FUNC(reputation_user_cmd)
sendnotice(client, "/REPUTATION <NN List users with reputation score below value NN"); sendnotice(client, "/REPUTATION <NN List users with reputation score below value NN");
return; return;
} }
if (strchr(parv[1], '.') || strchr(parv[1], ':')) if (strchr(parv[1], '.') || strchr(parv[1], ':'))
{ {
ip = parv[1]; ip = parv[1];
@ -871,7 +1160,7 @@ CMD_FUNC(reputation_user_cmd)
return; return;
} }
} }
e = find_reputation_entry(ip); e = find_reputation_entry(ip);
if (!e) if (!e)
{ {
@ -927,7 +1216,7 @@ CMD_FUNC(reputation_server_cmd)
sendnumeric(client, ERR_NEEDMOREPARAMS, "REPUTATION"); sendnumeric(client, ERR_NEEDMOREPARAMS, "REPUTATION");
return; return;
} }
ip = parv[1]; ip = parv[1];
if (parv[2][0] == '*') if (parv[2][0] == '*')
@ -993,7 +1282,7 @@ CMD_FUNC(reputation_cmd)
{ {
if (MyUser(client)) if (MyUser(client))
reputation_user_cmd(client, recv_mtags, parc, parv); reputation_user_cmd(client, recv_mtags, parc, parv);
else if (IsServer(client)) else if (IsServer(client) || IsMe(client))
reputation_server_cmd(client, recv_mtags, parc, parv); reputation_server_cmd(client, recv_mtags, parc, parv);
} }
@ -1003,7 +1292,7 @@ int reputation_whois(Client *client, Client *target)
if (!IsOper(client)) if (!IsOper(client))
return 0; /* only opers can see this.. */ return 0; /* only opers can see this.. */
if (reputation > 0) if (reputation > 0)
{ {
sendto_one(client, NULL, ":%s %d %s %s :is using an IP with a reputation score of %d", sendto_one(client, NULL, ":%s %d %s %s :is using an IP with a reputation score of %d",

View File

@ -36,6 +36,7 @@ struct RestrictedCommand {
int exempt_identified; int exempt_identified;
int exempt_reputation_score; int exempt_reputation_score;
int exempt_webirc; int exempt_webirc;
int exempt_tls;
}; };
typedef struct { typedef struct {
@ -179,9 +180,9 @@ int rcmd_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
if (!strcmp(cep2->ce_varname, "connect-delay")) if (!strcmp(cep2->ce_varname, "connect-delay"))
{ {
long v = config_checkval(cep2->ce_vardata, CFG_TIME); long v = config_checkval(cep2->ce_vardata, CFG_TIME);
if ((v < 10) || (v > 3600)) if ((v < 1) || (v > 3600))
{ {
config_error("%s:%i: set::restrict-commands::%s::connect-delay should be in range 10-3600", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, cep->ce_varname); config_error("%s:%i: set::restrict-commands::%s::connect-delay should be in range 1-3600", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, cep->ce_varname);
errors++; errors++;
} }
continue; continue;
@ -189,10 +190,13 @@ int rcmd_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
if (!strcmp(cep2->ce_varname, "exempt-identified")) if (!strcmp(cep2->ce_varname, "exempt-identified"))
continue; continue;
if (!strcmp(cep2->ce_varname, "exempt-webirc")) if (!strcmp(cep2->ce_varname, "exempt-webirc"))
continue; continue;
if (!strcmp(cep2->ce_varname, "exempt-tls"))
continue;
if (!strcmp(cep2->ce_varname, "exempt-reputation-score")) if (!strcmp(cep2->ce_varname, "exempt-reputation-score"))
{ {
int v = atoi(cep2->ce_vardata); int v = atoi(cep2->ce_vardata);
@ -279,6 +283,12 @@ int rcmd_configrun(ConfigFile *cf, ConfigEntry *ce, int type)
continue; continue;
} }
if (!strcmp(cep2->ce_varname, "exempt-tls"))
{
rcmd->exempt_tls = config_checkval(cep2->ce_vardata, CFG_YESNO);
continue;
}
if (!strcmp(cep2->ce_varname, "exempt-reputation-score")) if (!strcmp(cep2->ce_varname, "exempt-reputation-score"))
{ {
rcmd->exempt_reputation_score = atoi(cep2->ce_vardata); rcmd->exempt_reputation_score = atoi(cep2->ce_vardata);
@ -299,6 +309,8 @@ int rcmd_canbypass(Client *client, RestrictedCommand *rcmd)
return 1; return 1;
if (rcmd->exempt_webirc && moddata_client_get(client, "webirc")) if (rcmd->exempt_webirc && moddata_client_get(client, "webirc"))
return 1; return 1;
if (rcmd->exempt_tls && IsSecureConnect(client))
return 1;
if (rcmd->exempt_reputation_score > 0 && (GetReputation(client) >= rcmd->exempt_reputation_score)) if (rcmd->exempt_reputation_score > 0 && (GetReputation(client) >= rcmd->exempt_reputation_score))
return 1; return 1;
if (rcmd->connect_delay && client->local && (TStime() - client->local->firsttime >= rcmd->connect_delay)) if (rcmd->connect_delay && client->local && (TStime() - client->local->firsttime >= rcmd->connect_delay))

View File

@ -275,24 +275,5 @@ CMD_FUNC(cmd_sajoin)
strlcat(jbuf, ",", sizeof jbuf); strlcat(jbuf, ",", sizeof jbuf);
strlcat(jbuf, name, sizeof jbuf); strlcat(jbuf, name, sizeof jbuf);
} }
if (did_anything)
{
if (!sjmode)
{
//sendnotice(target, "*** You were forced to join %s", jbuf);
sendto_umode_global(UMODE_OPER, "%s used SAJOIN to make %s join %s", client->name, target->name, jbuf);
/* Logging function added by XeRXeS */
ircd_log(LOG_SACMDS,"SAJOIN: %s used SAJOIN to make %s join %s",
client->name, target->name, jbuf);
}
else
{
//sendnotice(target, "*** You were forced to join %s with '%c'", jbuf, sjmode);
sendto_umode_global(UMODE_OPER, "%s used SAJOIN to make %s join %c%s", client->name, target->name, sjmode, jbuf);
ircd_log(LOG_SACMDS,"SAJOIN: %s used SAJOIN to make %s join %c%s",
client->name, target->name, sjmode, jbuf);
}
}
} }
} }

View File

@ -159,22 +159,6 @@ CMD_FUNC(cmd_sapart)
parv[0] = target->name; // nick parv[0] = target->name; // nick
parv[1] = parv[2]; // chan parv[1] = parv[2]; // chan
parv[2] = comment ? commentx : NULL; // comment parv[2] = comment ? commentx : NULL; // comment
if (comment)
{
//sendnotice(target, "*** You were forced to part %s (%s)", parv[1], commentx);
sendto_umode_global(UMODE_OPER, "%s used SAPART to make %s part %s (%s)",
client->name, target->name, parv[1], comment);
ircd_log(LOG_SACMDS,"SAPART: %s used SAPART to make %s part %s (%s)",
client->name, target->name, parv[1], comment);
}
else
{
//sendnotice(target, "*** You were forced to part %s", parv[1]);
sendto_umode_global(UMODE_OPER, "%s used SAPART to make %s part %s",
client->name, target->name, parv[1]);
ircd_log(LOG_SACMDS,"SAPART: %s used SAPART to make %s part %s",
client->name, target->name, parv[1]);
}
do_cmd(target, NULL, "PART", comment ? 3 : 2, parv); do_cmd(target, NULL, "PART", comment ? 3 : 2, parv);
/* target may be killed now due to the part reason @ spamfilter */ /* target may be killed now due to the part reason @ spamfilter */
} }

View File

@ -118,6 +118,8 @@ CMD_FUNC(cmd_svslogin)
strlcpy(target->user->svid, parv[3], sizeof(target->user->svid)); strlcpy(target->user->svid, parv[3], sizeof(target->user->svid));
user_account_login(recv_mtags, target); user_account_login(recv_mtags, target);
if (MyConnect(target) && IsDead(target))
return; /* was killed due to *LINE on ~a probably */
} else { } else {
/* It is perfectly normal for target to be NULL as this /* It is perfectly normal for target to be NULL as this
* happens during registration phase (pre-connect). * happens during registration phase (pre-connect).

View File

@ -179,6 +179,7 @@ void _send_protoctl_servers(Client *client, int response)
{ {
char buf[512]; char buf[512];
Client *acptr; Client *acptr;
int sendit = 1;
sendto_one(client, NULL, "PROTOCTL EAUTH=%s,%d,%s%s,%s", sendto_one(client, NULL, "PROTOCTL EAUTH=%s,%d,%s%s,%s",
me.name, UnrealProtocol, serveropts, extraflags ? extraflags : "", version); me.name, UnrealProtocol, serveropts, extraflags ? extraflags : "", version);
@ -188,15 +189,24 @@ void _send_protoctl_servers(Client *client, int response)
list_for_each_entry(acptr, &global_server_list, client_node) list_for_each_entry(acptr, &global_server_list, client_node)
{ {
snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%s,", acptr->id); snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%s,", acptr->id);
sendit = 1;
if (strlen(buf) > sizeof(buf)-12) if (strlen(buf) > sizeof(buf)-12)
break; /* prevent overflow/cutoff if you have a network with more than 90 servers or something. */ {
if (buf[strlen(buf)-1] == ',')
buf[strlen(buf)-1] = '\0';
sendto_one(client, NULL, "%s", buf);
/* We use the asterisk here too for continuation lines */
ircsnprintf(buf, sizeof(buf), "PROTOCTL SERVERS=*");
sendit = 0;
}
} }
/* Remove final comma (if any) */ /* Remove final comma (if any) */
if (buf[strlen(buf)-1] == ',') if (buf[strlen(buf)-1] == ',')
buf[strlen(buf)-1] = '\0'; buf[strlen(buf)-1] = '\0';
sendto_one(client, NULL, "%s", buf); if (sendit)
sendto_one(client, NULL, "%s", buf);
} }
void _send_server_message(Client *client) void _send_server_message(Client *client)

View File

@ -89,7 +89,7 @@ CMD_FUNC(cmd_setname)
/* set the new name before we check, but don't send to servers unless it is ok */ /* set the new name before we check, but don't send to servers unless it is ok */
strcpy(client->info, parv[1]); strcpy(client->info, parv[1]);
spamfilter_build_user_string(spamfilter_user, client->name, client); spamfilter_build_user_string(spamfilter_user, client->name, client);
if (match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, 0, NULL)) if (match_spamfilter(client, spamfilter_user, SPAMF_USER, "SETNAME", NULL, 0, NULL))
{ {
/* Was rejected by spamfilter, restore the realname */ /* Was rejected by spamfilter, restore the realname */
strcpy(client->info, tmpinfo); strcpy(client->info, tmpinfo);

View File

@ -144,15 +144,6 @@ CMD_FUNC(cmd_squit)
sendto_umode_global(UMODE_OPER, "Received SQUIT %s from %s (%s)", sendto_umode_global(UMODE_OPER, "Received SQUIT %s from %s (%s)",
target->name, get_client_name(client, FALSE), comment); target->name, get_client_name(client, FALSE), comment);
} }
if (IsOper(client))
{
/*
* It was manually /squit'ed by a human being(we hope),
* there is a very good chance they don't want us to
* reconnect right away. -Cabal95
*/
SetSQuit(target);
}
exit_client(target, recv_mtags, comment); exit_client_ex(target, client->direction, recv_mtags, comment);
} }

View File

@ -376,8 +376,14 @@ CMD_FUNC(cmd_stats)
else else
stat->func(client, NULL); stat->func(client, NULL);
/* Modules can append data: */ /* Modules can append data:
RunHook2(HOOKTYPE_STATS, client, flags); * ('STATS S' already has special code for this that
* maintains certain ordering, so not included here)
*/
if (stat->flag != 'S')
{
RunHook2(HOOKTYPE_STATS, client, flags);
}
sendnumeric(client, RPL_ENDOFSTATS, stat->flag); sendnumeric(client, RPL_ENDOFSTATS, stat->flag);
@ -772,9 +778,33 @@ int stats_officialchannels(Client *client, char *para)
#define SafePrint(x) ((x) ? (x) : "") #define SafePrint(x) ((x) ? (x) : "")
/** Helper for stats_set() */
static void stats_set_anti_flood(Client *client, FloodSettings *f)
{
int i;
for (i=0; floodoption_names[i]; i++)
{
if (i == FLD_CONVERSATIONS)
{
sendtxtnumeric(client, "anti-flood::%s::%s: %d users, new user every %s",
f->name, floodoption_names[i],
(int)f->limit[i], pretty_time_val(f->period[i]));
}
else
{
sendtxtnumeric(client, "anti-flood::%s::%s: %d per %s",
f->name, floodoption_names[i],
(int)f->limit[i], pretty_time_val(f->period[i]));
}
}
}
int stats_set(Client *client, char *para) int stats_set(Client *client, char *para)
{ {
char *uhallow; char *uhallow;
SecurityGroup *s;
FloodSettings *f;
if (!ValidatePermissionsForPath("server:info:stats",client,NULL,NULL,NULL)) if (!ValidatePermissionsForPath("server:info:stats",client,NULL,NULL,NULL))
{ {
@ -875,9 +905,17 @@ int stats_set(Client *client, char *para)
sendtxtnumeric(client, "anti-flood::handshake-data-flood::amount: %ld bytes", iConf.handshake_data_flood_amount); sendtxtnumeric(client, "anti-flood::handshake-data-flood::amount: %ld bytes", iConf.handshake_data_flood_amount);
sendtxtnumeric(client, "anti-flood::handshake-data-flood::ban-action: %s", banact_valtostring(iConf.handshake_data_flood_ban_action)); sendtxtnumeric(client, "anti-flood::handshake-data-flood::ban-action: %s", banact_valtostring(iConf.handshake_data_flood_ban_action));
sendtxtnumeric(client, "anti-flood::handshake-data-flood::ban-time: %s", pretty_time_val(iConf.handshake_data_flood_ban_time)); sendtxtnumeric(client, "anti-flood::handshake-data-flood::ban-time: %s", pretty_time_val(iConf.handshake_data_flood_ban_time));
if (AWAY_PERIOD)
sendtxtnumeric(client, "anti-flood::away-flood: %d per %s", AWAY_COUNT, pretty_time_val(AWAY_PERIOD)); /* set::anti-flood */
sendtxtnumeric(client, "anti-flood::nick-flood: %d per %s", NICK_COUNT, pretty_time_val(NICK_PERIOD)); for (s = securitygroups; s; s = s->next)
if ((f = find_floodsettings_block(s->name)))
stats_set_anti_flood(client, f);
f = find_floodsettings_block("unknown-users");
stats_set_anti_flood(client, f);
//if (AWAY_PERIOD)
// sendtxtnumeric(client, "anti-flood::away-flood: %d per %s", AWAY_COUNT, pretty_time_val(AWAY_PERIOD));
//sendtxtnumeric(client, "anti-flood::nick-flood: %d per %s", NICK_COUNT, pretty_time_val(NICK_PERIOD));
sendtxtnumeric(client, "handshake-timeout: %s", pretty_time_val(iConf.handshake_timeout)); sendtxtnumeric(client, "handshake-timeout: %s", pretty_time_val(iConf.handshake_timeout));
sendtxtnumeric(client, "sasl-timeout: %s", pretty_time_val(iConf.sasl_timeout)); sendtxtnumeric(client, "sasl-timeout: %s", pretty_time_val(iConf.sasl_timeout));
sendtxtnumeric(client, "ident::connect-timeout: %s", pretty_time_val(IDENT_CONNECT_TIMEOUT)); sendtxtnumeric(client, "ident::connect-timeout: %s", pretty_time_val(IDENT_CONNECT_TIMEOUT));
@ -895,6 +933,10 @@ int stats_set(Client *client, char *para)
sendtxtnumeric(client, "outdated-tls-policy::oper: %s", policy_valtostr(iConf.outdated_tls_policy_oper)); sendtxtnumeric(client, "outdated-tls-policy::oper: %s", policy_valtostr(iConf.outdated_tls_policy_oper));
sendtxtnumeric(client, "outdated-tls-policy::server: %s", policy_valtostr(iConf.outdated_tls_policy_server)); sendtxtnumeric(client, "outdated-tls-policy::server: %s", policy_valtostr(iConf.outdated_tls_policy_server));
RunHook2(HOOKTYPE_STATS, client, "S"); RunHook2(HOOKTYPE_STATS, client, "S");
#ifndef _WIN32
sendtxtnumeric(client, "This server can handle %d concurrent sockets (%d clients + %d reserve)",
maxclients+CLIENTS_RESERVE, maxclients, CLIENTS_RESERVE);
#endif
return 1; return 1;
} }

View File

@ -408,6 +408,8 @@ void do_svsmode(Client *client, MessageTag *recv_mtags, int parc, char *parv[],
{ {
strlcpy(target->user->svid, parv[3], sizeof(target->user->svid)); strlcpy(target->user->svid, parv[3], sizeof(target->user->svid));
user_account_login(recv_mtags, target); user_account_login(recv_mtags, target);
if (MyConnect(target) && IsDead(target))
return; /* was killed due to *LINE on ~a probably */
} }
else else
{ {

View File

@ -95,6 +95,7 @@ CMD_FUNC(cmd_svsnick)
/* no 'recv_mtags' here, we do not inherit from SVSNICK but generate a new NICK event */ /* no 'recv_mtags' here, we do not inherit from SVSNICK but generate a new NICK event */
new_message(acptr, NULL, &mtags); new_message(acptr, NULL, &mtags);
RunHook3(HOOKTYPE_LOCAL_NICKCHANGE, acptr, mtags, parv[2]);
sendto_local_common_channels(acptr, acptr, 0, mtags, ":%s NICK :%s", acptr->name, parv[2]); sendto_local_common_channels(acptr, acptr, 0, mtags, ":%s NICK :%s", acptr->name, parv[2]);
sendto_one(acptr, mtags, ":%s NICK :%s", acptr->name, parv[2]); sendto_one(acptr, mtags, ":%s NICK :%s", acptr->name, parv[2]);
sendto_server(NULL, 0, 0, mtags, ":%s NICK %s :%ld", acptr->id, parv[2], atol(parv[3])); sendto_server(NULL, 0, 0, mtags, ":%s NICK %s :%ld", acptr->id, parv[2], atol(parv[3]));
@ -107,7 +108,6 @@ CMD_FUNC(cmd_svsnick)
sendto_snomask(SNO_NICKCHANGE, sendto_snomask(SNO_NICKCHANGE,
"*** %s (%s@%s) has been forced to change their nickname to %s", "*** %s (%s@%s) has been forced to change their nickname to %s",
acptr->name, acptr->user->username, acptr->user->realhost, parv[2]); acptr->name, acptr->user->username, acptr->user->realhost, parv[2]);
RunHook2(HOOKTYPE_LOCAL_NICKCHANGE, acptr, parv[2]);
strlcpy(acptr->name, parv[2], sizeof acptr->name); strlcpy(acptr->name, parv[2], sizeof acptr->name);
add_to_client_hash_table(parv[2], acptr); add_to_client_hash_table(parv[2], acptr);

View File

@ -234,8 +234,8 @@ int targetfloodprot_can_send_to_channel(Client *client, Channel *channel, Member
if (!MyUser(client)) if (!MyUser(client))
return HOOK_CONTINUE; return HOOK_CONTINUE;
/* IRCOps and U-Lines override */ /* U-Lines, servers and IRCOps override */
if (IsULine(client) || (IsOper(client) && ValidatePermissionsForPath("immune:target-flood",client,NULL,channel,NULL))) if (IsULine(client) || !IsUser(client) || (IsOper(client) && ValidatePermissionsForPath("immune:target-flood",client,NULL,channel,NULL)))
return HOOK_CONTINUE; return HOOK_CONTINUE;
what = sendtypetowhat(sendtype); what = sendtypetowhat(sendtype);
@ -259,6 +259,7 @@ int targetfloodprot_can_send_to_channel(Client *client, Channel *channel, Member
if (flood->cnt[what] >= channelcfg->cnt[what]) if (flood->cnt[what] >= channelcfg->cnt[what])
{ {
/* Flood detected */ /* Flood detected */
flood_limit_exceeded_log(client, "target-flood-channel");
snprintf(errbuf, sizeof(errbuf), "Channel is being flooded. Message not delivered."); snprintf(errbuf, sizeof(errbuf), "Channel is being flooded. Message not delivered.");
*errmsg = errbuf; *errmsg = errbuf;
return HOOK_DENY; return HOOK_DENY;
@ -280,8 +281,8 @@ int targetfloodprot_can_send_to_user(Client *client, Client *target, char **text
if (!MyUser(target)) if (!MyUser(target))
return HOOK_CONTINUE; return HOOK_CONTINUE;
/* IRCOps and U-Lines override */ /* U-Lines, servers and IRCOps override */
if (IsULine(client) || (IsOper(client) && ValidatePermissionsForPath("immune:target-flood",client,target,NULL,NULL))) if (IsULine(client) || !IsUser(client) || (IsOper(client) && ValidatePermissionsForPath("immune:target-flood",client,target,NULL,NULL)))
return HOOK_CONTINUE; return HOOK_CONTINUE;
what = sendtypetowhat(sendtype); what = sendtypetowhat(sendtype);
@ -305,6 +306,7 @@ int targetfloodprot_can_send_to_user(Client *client, Client *target, char **text
if (flood->cnt[what] >= privatecfg->cnt[what]) if (flood->cnt[what] >= privatecfg->cnt[what])
{ {
/* Flood detected */ /* Flood detected */
flood_limit_exceeded_log(client, "target-flood-user");
snprintf(errbuf, sizeof(errbuf), "User is being flooded. Message not delivered."); snprintf(errbuf, sizeof(errbuf), "User is being flooded. Message not delivered.");
*errmsg = errbuf; *errmsg = errbuf;
return HOOK_DENY; return HOOK_DENY;

View File

@ -82,7 +82,9 @@ void _tkl_stats(Client *client, int type, char *para, int *cnt);
void _tkl_sync(Client *client); void _tkl_sync(Client *client);
CMD_FUNC(_cmd_tkl); CMD_FUNC(_cmd_tkl);
int _place_host_ban(Client *client, BanAction action, char *reason, long duration); int _place_host_ban(Client *client, BanAction action, char *reason, long duration);
int _match_spamfilter(Client *client, char *str_in, int type, char *target, int flags, TKL **rettk); int _match_spamfilter(Client *client, char *str_in, int type, char *cmd, char *target, int flags, TKL **rettk);
int _match_spamfilter_mtags(Client *client, MessageTag *mtags, char *cmd);
int check_mtag_spamfilters_present(void);
int _join_viruschan(Client *client, TKL *tk, int type); int _join_viruschan(Client *client, TKL *tk, int type);
void _spamfilter_build_user_string(char *buf, char *nick, Client *client); void _spamfilter_build_user_string(char *buf, char *nick, Client *client);
int _match_user(char *rmask, Client *client, int options); int _match_user(char *rmask, Client *client, int options);
@ -109,6 +111,7 @@ struct TKLTypeTable
char *log_name; /**< Used for logging and server notices */ char *log_name; /**< Used for logging and server notices */
unsigned tkltype:1; /**< Is a type available in cmd_tkl() and friends */ unsigned tkltype:1; /**< Is a type available in cmd_tkl() and friends */
unsigned exceptiontype:1; /**< Is a type available for exceptions */ unsigned exceptiontype:1; /**< Is a type available for exceptions */
unsigned needip:1; /**< When using this exempt option, only IP addresses are permitted (processed before DNS/ident lookups etc) */
}; };
/** This table which defines all TKL types and TKL exception types. /** This table which defines all TKL types and TKL exception types.
@ -117,35 +120,35 @@ struct TKLTypeTable
* *
* IMPORTANT IF YOU ARE ADDING A NEW TYPE TO THIS TABLE: * IMPORTANT IF YOU ARE ADDING A NEW TYPE TO THIS TABLE:
* - also update eline_syntax() * - also update eline_syntax()
* - also check if eline_type_requires_ip() needs to be updated
* - update help.conf (HELPOP ELINE) * - update help.conf (HELPOP ELINE)
* - more? * - more?
*/ */
TKLTypeTable tkl_types[] = { TKLTypeTable tkl_types[] = {
/* <config name> <letter> <TKL_xxx type> <logging name> <tkl option?> <exempt option?> */ /* <config name> <letter> <TKL_xxx type> <logging name> <tkl option?> <exempt option?> <need ip address?> */
{ "gline", 'G', TKL_KILL | TKL_GLOBAL, "G-Line", 1, 1 }, { "gline", 'G', TKL_KILL | TKL_GLOBAL, "G-Line", 1, 1, 0 },
{ "kline", 'k', TKL_KILL, "K-Line", 1, 1 }, { "kline", 'k', TKL_KILL, "K-Line", 1, 1, 0 },
{ "gzline", 'Z', TKL_ZAP | TKL_GLOBAL, "Global Z-Line", 1, 1 }, { "gzline", 'Z', TKL_ZAP | TKL_GLOBAL, "Global Z-Line", 1, 1, 1 },
{ "zline", 'z', TKL_ZAP, "Z-Line", 1, 1 }, { "zline", 'z', TKL_ZAP, "Z-Line", 1, 1, 1 },
{ "spamfilter", 'F', TKL_SPAMF | TKL_GLOBAL, "Spamfilter", 1, 1 }, { "spamfilter", 'F', TKL_SPAMF | TKL_GLOBAL, "Spamfilter", 1, 1, 0 },
{ "qline", 'Q', TKL_NAME | TKL_GLOBAL, "Q-Line", 1, 1 }, { "qline", 'Q', TKL_NAME | TKL_GLOBAL, "Q-Line", 1, 1, 0 },
{ "except", 'E', TKL_EXCEPTION | TKL_GLOBAL, "Exception", 1, 0 }, { "except", 'E', TKL_EXCEPTION | TKL_GLOBAL, "Exception", 1, 0, 0 },
{ "shun", 's', TKL_SHUN | TKL_GLOBAL, "Shun", 1, 1 }, { "shun", 's', TKL_SHUN | TKL_GLOBAL, "Shun", 1, 1, 0 },
{ "local-qline", 'q', TKL_NAME, "Local Q-Line", 1, 0 }, { "local-qline", 'q', TKL_NAME, "Local Q-Line", 1, 0, 0 },
{ "local-spamfilter", 'e', TKL_EXCEPTION, "Local Exception", 1, 0 }, { "local-spamfilter", 'e', TKL_EXCEPTION, "Local Exception", 1, 0, 0 },
{ "local-exception", 'f', TKL_SPAMF, "Local Spamfilter", 1, 0 }, { "local-exception", 'f', TKL_SPAMF, "Local Spamfilter", 1, 0, 0 },
{ "blacklist", 'b', TKL_BLACKLIST, "Blacklist", 0, 1 }, { "blacklist", 'b', TKL_BLACKLIST, "Blacklist", 0, 1, 1 },
{ "connect-flood", 'c', TKL_CONNECT_FLOOD, "Connect flood", 0, 1 }, { "connect-flood", 'c', TKL_CONNECT_FLOOD, "Connect flood", 0, 1, 1 },
{ "maxperip", 'm', TKL_MAXPERIP, "Max-per-IP", 0, 1 }, { "maxperip", 'm', TKL_MAXPERIP, "Max-per-IP", 0, 1, 0 },
{ "handshake-data-flood", 'd', TKL_HANDSHAKE_DATA_FLOOD, "Handshake data flood", 0, 1 }, { "handshake-data-flood", 'd', TKL_HANDSHAKE_DATA_FLOOD, "Handshake data flood", 0, 1, 1 },
{ "antirandom", 'r', TKL_ANTIRANDOM, "Antirandom", 0, 1 }, { "antirandom", 'r', TKL_ANTIRANDOM, "Antirandom", 0, 1, 0 },
{ "antimixedutf8", '8', TKL_ANTIMIXEDUTF8, "Antimixedutf8", 0, 1 }, { "antimixedutf8", '8', TKL_ANTIMIXEDUTF8, "Antimixedutf8", 0, 1, 0 },
{ "ban-version", 'v', TKL_BAN_VERSION, "Ban Version", 0, 1 }, { "ban-version", 'v', TKL_BAN_VERSION, "Ban Version", 0, 1, 0 },
{ NULL, '\0', 0, NULL, 0, 0 }, { NULL, '\0', 0, NULL, 0, 0, 0 },
}; };
#define ALL_VALID_EXCEPTION_TYPES "kline, gline, zline, gzline, spamfilter, shun, qline, blacklist, connect-flood, handshake-data-flood, antirandom, antimixedutf8, ban-version" #define ALL_VALID_EXCEPTION_TYPES "kline, gline, zline, gzline, spamfilter, shun, qline, blacklist, connect-flood, handshake-data-flood, antirandom, antimixedutf8, ban-version"
int max_stats_matches = 1000; int max_stats_matches = 1000;
int mtag_spamfilters_present = 0; /**< Are any spamfilters with type SPAMF_MTAG present? */
MOD_TEST() MOD_TEST()
{ {
@ -178,8 +181,9 @@ MOD_TEST()
EfunctionAddVoid(modinfo->handle, EFUNC_TKL_SYNCH, _tkl_sync); EfunctionAddVoid(modinfo->handle, EFUNC_TKL_SYNCH, _tkl_sync);
EfunctionAddVoid(modinfo->handle, EFUNC_CMD_TKL, _cmd_tkl); EfunctionAddVoid(modinfo->handle, EFUNC_CMD_TKL, _cmd_tkl);
EfunctionAdd(modinfo->handle, EFUNC_PLACE_HOST_BAN, _place_host_ban); EfunctionAdd(modinfo->handle, EFUNC_PLACE_HOST_BAN, _place_host_ban);
EfunctionAdd(modinfo->handle, EFUNC_DOSPAMFILTER, _match_spamfilter); EfunctionAdd(modinfo->handle, EFUNC_MATCH_SPAMFILTER, _match_spamfilter);
EfunctionAdd(modinfo->handle, EFUNC_DOSPAMFILTER_VIRUSCHAN, _join_viruschan); EfunctionAdd(modinfo->handle, EFUNC_MATCH_SPAMFILTER_MTAGS, _match_spamfilter_mtags);
EfunctionAdd(modinfo->handle, EFUNC_JOIN_VIRUSCHAN, _join_viruschan);
EfunctionAddVoid(modinfo->handle, EFUNC_SPAMFILTER_BUILD_USER_STRING, _spamfilter_build_user_string); EfunctionAddVoid(modinfo->handle, EFUNC_SPAMFILTER_BUILD_USER_STRING, _spamfilter_build_user_string);
EfunctionAdd(modinfo->handle, EFUNC_MATCH_USER, _match_user); EfunctionAdd(modinfo->handle, EFUNC_MATCH_USER, _match_user);
EfunctionAdd(modinfo->handle, EFUNC_TKL_IP_HASH, _tkl_ip_hash); EfunctionAdd(modinfo->handle, EFUNC_TKL_IP_HASH, _tkl_ip_hash);
@ -213,6 +217,7 @@ MOD_INIT()
MOD_LOAD() MOD_LOAD()
{ {
check_mtag_spamfilters_present();
EventAdd(modinfo->handle, "tklexpire", tkl_check_expire, NULL, 5000, 0); EventAdd(modinfo->handle, "tklexpire", tkl_check_expire, NULL, 5000, 0);
return MOD_SUCCESS; return MOD_SUCCESS;
} }
@ -1363,6 +1368,15 @@ void cmd_tkl_line(Client *client, int parc, char *parv[], char *type)
mask[3] = '\0'; mask[3] = '\0';
usermask = mask; /* eg ~S: */ usermask = mask; /* eg ~S: */
hostmask = mask2buf; hostmask = mask2buf;
if (((*type == 'z') || (*type == 'Z')))
{
sendnotice(client, "ERROR: (g)zlines must be placed at *@\037IPMASK\037. "
"Extended server bans don't work here because (g)zlines are processed"
"BEFORE dns and ident lookups are done and before reading any client data. "
"If you want to use extended server bans then use a KLINE/GLINE instead.");
return;
}
} else { } else {
/* Delete: allow any attempt */ /* Delete: allow any attempt */
strlcpy(mask2buf, mask+3, sizeof(mask2buf)); strlcpy(mask2buf, mask+3, sizeof(mask2buf));
@ -1543,14 +1557,14 @@ void eline_syntax(Client *client)
* exception to be placed on *@ip rather than * exception to be placed on *@ip rather than
* user@host or *@host. For eg zlines. * user@host or *@host. For eg zlines.
*/ */
int eline_type_requires_ip(char *bantypes) TKLTypeTable *eline_type_requires_ip(char *bantypes)
{ {
if (strchr(bantypes, 'z') || strchr(bantypes, 'Z') || int i;
strchr(bantypes, 'c') ||
strchr(bantypes, 'b') || for (i=0; tkl_types[i].config_name; i++)
strchr(bantypes, 'd')) if (tkl_types[i].needip && strchr(bantypes, tkl_types[i].letter))
return 1; return &tkl_types[i];
return 0; return NULL;
} }
/** Checks a string to see if it contains invalid ban exception types */ /** Checks a string to see if it contains invalid ban exception types */
@ -1590,6 +1604,7 @@ CMD_FUNC(cmd_eline)
"-", /*9 reason */ "-", /*9 reason */
NULL NULL
}; };
TKLTypeTable *t;
if (IsServer(client)) if (IsServer(client))
return; return;
@ -1673,11 +1688,11 @@ CMD_FUNC(cmd_eline)
mask[3] = '\0'; mask[3] = '\0';
usermask = mask; /* eg ~S: */ usermask = mask; /* eg ~S: */
hostmask = mask2buf; hostmask = mask2buf;
if (eline_type_requires_ip(bantypes)) if ((t = eline_type_requires_ip(bantypes)))
{ {
sendnotice(client, "ERROR: Ban exceptions with type z/Z/c/b do not work on extended server bans. " sendnotice(client, "ERROR: Ban exception with type '%c' does not work on extended server bans. "
"This is because checking (g)zlines, connect-flood and blacklists is done BEFORE " "This is because checking for %s takes places BEFORE "
"extended bans can be checked."); "extended bans can be checked.", t->letter, t->log_name);
return; return;
} }
} else { } else {
@ -1724,25 +1739,31 @@ CMD_FUNC(cmd_eline)
sendnotice(client, "[error] For technical reasons you cannot start the host with a ':', sorry"); sendnotice(client, "[error] For technical reasons you cannot start the host with a ':', sorry");
return; return;
} }
if (add && eline_type_requires_ip(bantypes)) if (add && ((t = eline_type_requires_ip(bantypes))))
{ {
/* Trying to exempt a user from a (G)ZLINE, /* Trying to exempt a user from a (G)ZLINE,
* make sure the user isn't specifying a host then. * make sure the user isn't specifying a host then.
*/ */
if (strcmp(usermask, "*")) if (strcmp(usermask, "*"))
{ {
sendnotice(client, "ERROR: Ban exceptions with type z/Z/c/b need to be placed at \037*\037@ipmask, not \037user\037@ipmask. " sendnotice(client, "ERROR: Ban exception with type '%c' need to be placed at \037*\037@ipmask, not \037user\037@ipmask. "
"This is because checking (g)zlines, connect-flood and blacklists is done BEFORE any dns and ident lookups."); "This is because checking %s takes places (possibly) BEFORE any dns and ident lookups.",
t->letter,
t->log_name);
return; return;
} }
for (p=hostmask; *p; p++) for (p=hostmask; *p; p++)
{
if (isalpha(*p) && !isxdigit(*p)) if (isalpha(*p) && !isxdigit(*p))
{ {
sendnotice(client, "ERROR: Ban exceptions with type z/Z/c/b need to be placed at *@\037ipmask\037, not *@\037hostmask\037. " sendnotice(client, "ERROR: Ban exception with type '%c' needs to be placed at *@\037ipmask\037, not *@\037hostmask\037. "
"(so for example *@192.168.* is ok, but *@*.aol.com is not). " "(so for example *@192.168.* is OK, but *@*.aol.com is not). "
"This is because checking (g)zlines, connect-flood and blacklists is done BEFORE any dns and ident lookups."); "This is because checking %s takes places (possibly) BEFORE any dns and ident lookups.",
t->letter,
t->log_name);
return; return;
} }
}
} }
} }
else else
@ -2367,6 +2388,9 @@ TKL *_tkl_add_spamfilter(int type, unsigned short target, BanAction action, Matc
index = tkl_hash(tkl_typetochar(type)); index = tkl_hash(tkl_typetochar(type));
AddListItem(tkl, tklines[index]); AddListItem(tkl, tklines[index]);
if (target & SPAMF_MTAG)
mtag_spamfilters_present = 1;
return tkl; return tkl;
} }
@ -2637,6 +2661,7 @@ void _tkl_del_line(TKL *tkl)
/* Finally, free the entry */ /* Finally, free the entry */
free_tkl(tkl); free_tkl(tkl);
check_mtag_spamfilters_present();
} }
/** Add some default ban exceptions - for localhost */ /** Add some default ban exceptions - for localhost */
@ -3052,7 +3077,7 @@ int _find_shun(Client *client)
if (!(tkl->type & TKL_SHUN)) if (!(tkl->type & TKL_SHUN))
continue; continue;
snprintf(uhost, sizeof(uhost), "%s@%s", tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask); tkl_uhost(tkl, uhost, sizeof(uhost), NO_SOFT_PREFIX);
if (match_user(uhost, client, MATCH_CHECK_REAL)) if (match_user(uhost, client, MATCH_CHECK_REAL))
{ {
@ -3114,7 +3139,7 @@ int _find_spamfilter_user(Client *client, int flags)
return 0; return 0;
spamfilter_build_user_string(spamfilter_user, client->name, client); spamfilter_build_user_string(spamfilter_user, client->name, client);
return match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, flags, NULL); return match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, NULL, flags, NULL);
} }
/** Check a spamfilter against all local users and print a message. /** Check a spamfilter against all local users and print a message.
@ -4654,6 +4679,7 @@ int _join_viruschan(Client *client, TKL *tkl, int type)
/** match_spamfilter: executes the spamfilter on the input string. /** match_spamfilter: executes the spamfilter on the input string.
* @param str The text (eg msg text, notice text, part text, quit text, etc * @param str The text (eg msg text, notice text, part text, quit text, etc
* @param target The spamfilter target (SPAMF_*) * @param target The spamfilter target (SPAMF_*)
* @param cmd The command (eg: "PRIVMSG")
* @param destination The destination as a text string (eg: "somenick", can be NULL.. eg for away) * @param destination The destination as a text string (eg: "somenick", can be NULL.. eg for away)
* @param flags Any flags (SPAMFLAG_*) * @param flags Any flags (SPAMFLAG_*)
* @param rettkl Pointer to an aTKLline struct, _used for special circumstances only_ * @param rettkl Pointer to an aTKLline struct, _used for special circumstances only_
@ -4661,7 +4687,7 @@ int _join_viruschan(Client *client, TKL *tkl, int type)
* 1 if spamfilter matched and it should be blocked (or client exited), 0 if not matched. * 1 if spamfilter matched and it should be blocked (or client exited), 0 if not matched.
* In case of 1, be sure to check IsDead(client).. * In case of 1, be sure to check IsDead(client)..
*/ */
int _match_spamfilter(Client *client, char *str_in, int target, char *destination, int flags, TKL **rettkl) int _match_spamfilter(Client *client, char *str_in, int target, char *cmd, char *destination, int flags, TKL **rettkl)
{ {
TKL *tkl; TKL *tkl;
TKL *winner_tkl = NULL; TKL *winner_tkl = NULL;
@ -4676,6 +4702,9 @@ int _match_spamfilter(Client *client, char *str_in, int target, char *destinatio
if (rettkl) if (rettkl)
*rettkl = NULL; /* initialize to NULL */ *rettkl = NULL; /* initialize to NULL */
if (!cmd)
cmd = cmdname_by_spamftarget(target);
if (target == SPAMF_USER) if (target == SPAMF_USER)
str = str_in; str = str_in;
else else
@ -4755,7 +4784,7 @@ int _match_spamfilter(Client *client, char *str_in, int target, char *destinatio
ircsnprintf(buf, sizeof(buf), "[Spamfilter] %s!%s@%s matches filter '%s': [%s%s: '%s'] [%s]", ircsnprintf(buf, sizeof(buf), "[Spamfilter] %s!%s@%s matches filter '%s': [%s%s: '%s'] [%s]",
client->name, client->user->username, client->user->realhost, client->name, client->user->username, client->user->realhost,
tkl->ptr.spamfilter->match->str, tkl->ptr.spamfilter->match->str,
cmdname_by_spamftarget(target), destinationbuf, str, cmd, destinationbuf, str,
unreal_decodespace(tkl->ptr.spamfilter->tkl_reason)); unreal_decodespace(tkl->ptr.spamfilter->tkl_reason));
sendto_snomask_global(SNO_SPAMF, "%s", buf); sendto_snomask_global(SNO_SPAMF, "%s", buf);
@ -4807,6 +4836,12 @@ int _match_spamfilter(Client *client, char *str_in, int target, char *destinatio
me.name, client->name, destination, reason); me.name, client->name, destination, reason);
break; break;
} }
case SPAMF_MTAG:
{
sendnumericfmt(client, ERR_CANNOTDOCOMMAND, "%s :Command blocked: %s",
cmd, reason);
break;
}
case SPAMF_DCC: case SPAMF_DCC:
{ {
char errmsg[512]; char errmsg[512];
@ -4837,7 +4872,7 @@ int _match_spamfilter(Client *client, char *str_in, int target, char *destinatio
if ((tkl->ptr.spamfilter->action == BAN_ACT_WARN) || (tkl->ptr.spamfilter->action == BAN_ACT_SOFT_WARN)) if ((tkl->ptr.spamfilter->action == BAN_ACT_WARN) || (tkl->ptr.spamfilter->action == BAN_ACT_SOFT_WARN))
{ {
if ((target != SPAMF_USER) && (target != SPAMF_QUIT)) if ((target != SPAMF_USER) && (target != SPAMF_QUIT))
sendnumeric(client, RPL_SPAMCMDFWD, cmdname_by_spamftarget(target), reason); sendnumeric(client, RPL_SPAMCMDFWD, cmd, reason);
return 0; return 0;
} else } else
if ((tkl->ptr.spamfilter->action == BAN_ACT_DCCBLOCK) || (tkl->ptr.spamfilter->action == BAN_ACT_SOFT_DCCBLOCK)) if ((tkl->ptr.spamfilter->action == BAN_ACT_DCCBLOCK) || (tkl->ptr.spamfilter->action == BAN_ACT_SOFT_DCCBLOCK))
@ -4875,6 +4910,60 @@ int _match_spamfilter(Client *client, char *str_in, int target, char *destinatio
return 0; /* NOTREACHED */ return 0; /* NOTREACHED */
} }
/** Check message-tag spamfilters.
* @param client The client
* @param mtags Message tags sent by client
* @param cmd Command to be executed (can be NULL)
* @retval Return 1 to stop processing the command (ignore it) or 0 to allow/continue as normal
*/
int _match_spamfilter_mtags(Client *client, MessageTag *mtags, char *cmd)
{
MessageTag *m;
char buf[4096];
char *str;
/* This is a shortcut: if there are no spamfilters present
* on message tags then we can return immediately.
* Saves a lot of CPU and it is quite likely too!
*/
if (mtag_spamfilters_present == 0)
return 0;
for (m = mtags; m; m = m->next)
{
if (m->value)
{
snprintf(buf, sizeof(buf), "%s=%s", m->name, m->value);
str = buf;
} else {
str = m->name;
}
if (match_spamfilter(client, str, SPAMF_MTAG, cmd, NULL, 0, NULL))
return 1;
}
return 0;
}
/** Updates 'mtag_spamfilters_present' based on if any spamfilters
* are present with the SPAMF_MTAG target.
*/
int check_mtag_spamfilters_present(void)
{
TKL *tkl;
for (tkl = tklines[tkl_hash('F')]; tkl; tkl = tkl->next)
{
if (tkl->ptr.spamfilter->target & SPAMF_MTAG)
{
mtag_spamfilters_present = 1;
return 1;
}
}
mtag_spamfilters_present = 0;
return 0;
}
/** CIDR function to compare the first 'mask' bits. /** CIDR function to compare the first 'mask' bits.
* @author Taken from atheme * @author Taken from atheme
* @returns 1 if equal, 0 if not. * @returns 1 if equal, 0 if not.

View File

@ -61,14 +61,14 @@ ModuleHeader MOD_HEADER = {
do { \ do { \
sendto_realops_and_log("[tkldb] Error writing to temporary database file " \ sendto_realops_and_log("[tkldb] Error writing to temporary database file " \
"'%s': %s (DATABASE NOT SAVED)", \ "'%s': %s (DATABASE NOT SAVED)", \
fname, strerror(errno)); \ fname, unrealdb_get_error_string()); \
} while(0) } while(0)
#define R_SAFE(x) \ #define R_SAFE(x) \
do { \ do { \
if (!(x)) { \ if (!(x)) { \
config_warn("[tkldb] Read error from database file '%s' (possible corruption): %s", cfg.database, strerror(errno)); \ config_warn("[tkldb] Read error from database file '%s' (possible corruption): %s", cfg.database, unrealdb_get_error_string()); \
fclose(fd); \ unrealdb_close(db); \
FreeTKLRead(); \ FreeTKLRead(); \
return 0; \ return 0; \
} \ } \
@ -78,7 +78,7 @@ ModuleHeader MOD_HEADER = {
do { \ do { \
if (!(x)) { \ if (!(x)) { \
WARN_WRITE_ERROR(tmpfname); \ WARN_WRITE_ERROR(tmpfname); \
fclose(fd); \ unrealdb_close(db); \
return 0; \ return 0; \
} \ } \
} while(0) } while(0)
@ -91,41 +91,56 @@ ModuleHeader MOD_HEADER = {
} \ } \
} while(0) } while(0)
/* Structs */
struct cfgstruct {
char *database;
char *db_secret;
};
/* Forward declarations */ /* Forward declarations */
void tkldb_moddata_free(ModData *md); void tkldb_moddata_free(ModData *md);
void setcfg(void); void setcfg(struct cfgstruct *cfg);
void freecfg(void); void freecfg(struct cfgstruct *cfg);
int tkldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); int tkldb_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
int tkldb_configrun(ConfigFile *cf, ConfigEntry *ce, int type); int tkldb_config_posttest(int *errs);
int tkldb_config_run(ConfigFile *cf, ConfigEntry *ce, int type);
EVENT(write_tkldb_evt); EVENT(write_tkldb_evt);
int write_tkldb(void); int write_tkldb(void);
int write_tkline(FILE *fd, const char *tmpfname, TKL *tkl); int write_tkline(UnrealDB *db, const char *tmpfname, TKL *tkl);
int read_tkldb(void); int read_tkldb(void);
/* Globals variables */ /* Globals variables */
const uint32_t tkldb_version = TKLDB_VERSION; const uint32_t tkldb_version = TKLDB_VERSION;
struct cfgstruct {
char *database;
};
static struct cfgstruct cfg; static struct cfgstruct cfg;
static struct cfgstruct test;
static long tkldb_next_event = 0; static long tkldb_next_event = 0;
MOD_TEST() MOD_TEST()
{ {
memset(&cfg, 0, sizeof(cfg)); memset(&cfg, 0, sizeof(cfg));
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, tkldb_configtest); memset(&test, 0, sizeof(test));
setcfg(&test);
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, tkldb_config_test);
HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, tkldb_config_posttest);
return MOD_SUCCESS; return MOD_SUCCESS;
} }
MOD_INIT() MOD_INIT()
{ {
MARK_AS_OFFICIAL_MODULE(modinfo); MARK_AS_OFFICIAL_MODULE(modinfo);
ModuleSetOptions(modinfo->handle, MOD_OPT_UNLOAD_PRIORITY, -9999);
LoadPersistentLong(modinfo, tkldb_next_event); LoadPersistentLong(modinfo, tkldb_next_event);
setcfg(); setcfg(&cfg);
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkldb_config_run);
return MOD_SUCCESS;
}
MOD_LOAD()
{
if (!tkldb_next_event) if (!tkldb_next_event)
{ {
/* If this is the first time that our module is loaded, then /* If this is the first time that our module is loaded, then
@ -142,25 +157,16 @@ MOD_INIT()
} }
tkldb_next_event = TStime() + TKLDB_SAVE_EVERY + TKLDB_SAVE_EVERY_DELTA; tkldb_next_event = TStime() + TKLDB_SAVE_EVERY + TKLDB_SAVE_EVERY_DELTA;
} }
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkldb_configrun);
return MOD_SUCCESS;
}
MOD_LOAD()
{
EventAdd(modinfo->handle, "tkldb_write_tkldb", write_tkldb_evt, NULL, 1000, 0); EventAdd(modinfo->handle, "tkldb_write_tkldb", write_tkldb_evt, NULL, 1000, 0);
if (ModuleGetError(modinfo->handle) != MODERR_NOERROR)
{
config_error("A critical error occurred when loading module %s: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle));
return MOD_FAILED;
}
return MOD_SUCCESS; return MOD_SUCCESS;
} }
MOD_UNLOAD() MOD_UNLOAD()
{ {
write_tkldb(); if (loop.ircd_terminating)
freecfg(); write_tkldb();
freecfg(&test);
freecfg(&cfg);
SavePersistentLong(modinfo, tkldb_next_event); SavePersistentLong(modinfo, tkldb_next_event);
return MOD_SUCCESS; return MOD_SUCCESS;
} }
@ -171,19 +177,20 @@ void tkldb_moddata_free(ModData *md)
md->i = 0; md->i = 0;
} }
void setcfg(void) void setcfg(struct cfgstruct *cfg)
{ {
// Default: data/tkl.db // Default: data/tkl.db
safe_strdup(cfg.database, "tkl.db"); safe_strdup(cfg->database, "tkl.db");
convert_to_absolute_path(&cfg.database, PERMDATADIR); convert_to_absolute_path(&cfg->database, PERMDATADIR);
} }
void freecfg(void) void freecfg(struct cfgstruct *cfg)
{ {
safe_free(cfg.database); safe_free(cfg->database);
safe_free(cfg->db_secret);
} }
int tkldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) int tkldb_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
{ {
int errors = 0; int errors = 0;
ConfigEntry *cep; ConfigEntry *cep;
@ -197,16 +204,45 @@ int tkldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
for (cep = ce->ce_entries; cep; cep = cep->ce_next) for (cep = ce->ce_entries; cep; cep = cep->ce_next)
{ {
if (!cep->ce_vardata) { if (!cep->ce_vardata)
{
config_error("%s:%i: blank set::tkldb::%s without value", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); config_error("%s:%i: blank set::tkldb::%s without value", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname);
errors++; errors++;
continue; } else
} if (!strcmp(cep->ce_varname, "database"))
if (!strcmp(cep->ce_varname, "database")) { {
convert_to_absolute_path(&cep->ce_vardata, PERMDATADIR); convert_to_absolute_path(&cep->ce_vardata, PERMDATADIR);
continue; safe_strdup(test.database, cep->ce_vardata);
} else
if (!strcmp(cep->ce_varname, "db-secret"))
{
char *err;
if ((err = unrealdb_test_secret(cep->ce_vardata)))
{
config_error("%s:%i: set::tkldb::db-secret: %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, err);
errors++;
continue;
}
safe_strdup(test.db_secret, cep->ce_vardata);
} else
{
config_error("%s:%i: unknown directive set::tkldb::%s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname);
errors++;
} }
config_error("%s:%i: unknown directive set::tkldb::%s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); }
*errs = errors;
return errors ? -1 : 1;
}
int tkldb_config_posttest(int *errs)
{
int errors = 0;
char *errstr;
if (test.database && ((errstr = unrealdb_test_db(test.database, test.db_secret))))
{
config_error("[tkldb] %s", errstr);
errors++; errors++;
} }
@ -214,7 +250,7 @@ int tkldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
return errors ? -1 : 1; return errors ? -1 : 1;
} }
int tkldb_configrun(ConfigFile *cf, ConfigEntry *ce, int type) int tkldb_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
{ {
ConfigEntry *cep; ConfigEntry *cep;
@ -229,6 +265,8 @@ int tkldb_configrun(ConfigFile *cf, ConfigEntry *ce, int type)
{ {
if (!strcmp(cep->ce_varname, "database")) if (!strcmp(cep->ce_varname, "database"))
safe_strdup(cfg.database, cep->ce_vardata); safe_strdup(cfg.database, cep->ce_vardata);
else if (!strcmp(cep->ce_varname, "db-secret"))
safe_strdup(cfg.db_secret, cep->ce_vardata);
} }
return 1; return 1;
} }
@ -244,7 +282,7 @@ EVENT(write_tkldb_evt)
int write_tkldb(void) int write_tkldb(void)
{ {
char tmpfname[512]; char tmpfname[512];
FILE *fd; UnrealDB *db;
uint64_t tklcount; uint64_t tklcount;
int index, index2; int index, index2;
TKL *tkl; TKL *tkl;
@ -256,15 +294,15 @@ int write_tkldb(void)
// Write to a tempfile first, then rename it if everything succeeded // Write to a tempfile first, then rename it if everything succeeded
snprintf(tmpfname, sizeof(tmpfname), "%s.%x.tmp", cfg.database, getrandom32()); snprintf(tmpfname, sizeof(tmpfname), "%s.%x.tmp", cfg.database, getrandom32());
fd = fopen(tmpfname, "wb"); db = unrealdb_open(tmpfname, UNREALDB_MODE_WRITE, cfg.db_secret);
if (!fd) if (!db)
{ {
WARN_WRITE_ERROR(tmpfname); WARN_WRITE_ERROR(tmpfname);
return 0; return 0;
} }
W_SAFE(write_int32(fd, TKLDB_MAGIC)); W_SAFE(unrealdb_write_int32(db, TKLDB_MAGIC));
W_SAFE(write_data(fd, &tkldb_version, sizeof(tkldb_version))); W_SAFE(unrealdb_write_int32(db, tkldb_version));
// Count the *-Lines // Count the *-Lines
tklcount = 0; tklcount = 0;
@ -292,7 +330,7 @@ int write_tkldb(void)
tklcount++; tklcount++;
} }
} }
W_SAFE(write_data(fd, &tklcount, sizeof(tklcount))); W_SAFE(unrealdb_write_int64(db, tklcount));
// Now write the actual *-Lines, first the ones in the hash table // Now write the actual *-Lines, first the ones in the hash table
for (index = 0; index < TKLIPHASHLEN1; index++) for (index = 0; index < TKLIPHASHLEN1; index++)
@ -303,7 +341,7 @@ int write_tkldb(void)
{ {
if (tkl->flags & TKL_FLAG_CONFIG) if (tkl->flags & TKL_FLAG_CONFIG)
continue; /* config entry */ continue; /* config entry */
if (!write_tkline(fd, tmpfname, tkl)) // write_tkline() closes the fd on errors itself if (!write_tkline(db, tmpfname, tkl)) // write_tkline() closes the db on errors itself
return 0; return 0;
} }
} }
@ -315,13 +353,13 @@ int write_tkldb(void)
{ {
if (tkl->flags & TKL_FLAG_CONFIG) if (tkl->flags & TKL_FLAG_CONFIG)
continue; /* config entry */ continue; /* config entry */
if (!write_tkline(fd, tmpfname, tkl)) if (!write_tkline(db, tmpfname, tkl))
return 0; return 0;
} }
} }
// Everything seems to have gone well, attempt to close and rename the tempfile // Everything seems to have gone well, attempt to close and rename the tempfile
if (fclose(fd) != 0) if (!unrealdb_close(db))
{ {
WARN_WRITE_ERROR(tmpfname); WARN_WRITE_ERROR(tmpfname);
return 0; return 0;
@ -344,18 +382,18 @@ int write_tkldb(void)
} }
/** Write a TKL entry */ /** Write a TKL entry */
int write_tkline(FILE *fd, const char *tmpfname, TKL *tkl) int write_tkline(UnrealDB *db, const char *tmpfname, TKL *tkl)
{ {
char tkltype; char tkltype;
char buf[256]; char buf[256];
/* First, write the common attributes */ /* First, write the common attributes */
tkltype = tkl_typetochar(tkl->type); tkltype = tkl_typetochar(tkl->type);
W_SAFE(write_data(fd, &tkltype, sizeof(tkltype))); // TKL char W_SAFE(unrealdb_write_char(db, tkltype)); // TKL char
W_SAFE(write_str(fd, tkl->set_by)); W_SAFE(unrealdb_write_str(db, tkl->set_by));
W_SAFE(write_int64(fd, tkl->set_at)); W_SAFE(unrealdb_write_int64(db, tkl->set_at));
W_SAFE(write_int64(fd, tkl->expire_at)); W_SAFE(unrealdb_write_int64(db, tkl->expire_at));
if (TKLIsServerBan(tkl)) if (TKLIsServerBan(tkl))
{ {
@ -365,9 +403,9 @@ int write_tkline(FILE *fd, const char *tmpfname, TKL *tkl)
snprintf(buf, sizeof(buf), "%%%s", tkl->ptr.serverban->usermask); snprintf(buf, sizeof(buf), "%%%s", tkl->ptr.serverban->usermask);
usermask = buf; usermask = buf;
} }
W_SAFE(write_str(fd, usermask)); W_SAFE(unrealdb_write_str(db, usermask));
W_SAFE(write_str(fd, tkl->ptr.serverban->hostmask)); W_SAFE(unrealdb_write_str(db, tkl->ptr.serverban->hostmask));
W_SAFE(write_str(fd, tkl->ptr.serverban->reason)); W_SAFE(unrealdb_write_str(db, tkl->ptr.serverban->reason));
} else } else
if (TKLIsBanException(tkl)) if (TKLIsBanException(tkl))
{ {
@ -377,17 +415,17 @@ int write_tkline(FILE *fd, const char *tmpfname, TKL *tkl)
snprintf(buf, sizeof(buf), "%%%s", tkl->ptr.banexception->usermask); snprintf(buf, sizeof(buf), "%%%s", tkl->ptr.banexception->usermask);
usermask = buf; usermask = buf;
} }
W_SAFE(write_str(fd, usermask)); W_SAFE(unrealdb_write_str(db, usermask));
W_SAFE(write_str(fd, tkl->ptr.banexception->hostmask)); W_SAFE(unrealdb_write_str(db, tkl->ptr.banexception->hostmask));
W_SAFE(write_str(fd, tkl->ptr.banexception->bantypes)); W_SAFE(unrealdb_write_str(db, tkl->ptr.banexception->bantypes));
W_SAFE(write_str(fd, tkl->ptr.banexception->reason)); W_SAFE(unrealdb_write_str(db, tkl->ptr.banexception->reason));
} else } else
if (TKLIsNameBan(tkl)) if (TKLIsNameBan(tkl))
{ {
char *hold = tkl->ptr.nameban->hold ? "H" : "*"; char *hold = tkl->ptr.nameban->hold ? "H" : "*";
W_SAFE(write_str(fd, hold)); W_SAFE(unrealdb_write_str(db, hold));
W_SAFE(write_str(fd, tkl->ptr.nameban->name)); W_SAFE(unrealdb_write_str(db, tkl->ptr.nameban->name));
W_SAFE(write_str(fd, tkl->ptr.nameban->reason)); W_SAFE(unrealdb_write_str(db, tkl->ptr.nameban->reason));
} else } else
if (TKLIsSpamfilter(tkl)) if (TKLIsSpamfilter(tkl))
{ {
@ -395,12 +433,12 @@ int write_tkline(FILE *fd, const char *tmpfname, TKL *tkl)
char *target = spamfilter_target_inttostring(tkl->ptr.spamfilter->target); char *target = spamfilter_target_inttostring(tkl->ptr.spamfilter->target);
char action = banact_valtochar(tkl->ptr.spamfilter->action); char action = banact_valtochar(tkl->ptr.spamfilter->action);
W_SAFE(write_str(fd, match_type)); W_SAFE(unrealdb_write_str(db, match_type));
W_SAFE(write_str(fd, tkl->ptr.spamfilter->match->str)); W_SAFE(unrealdb_write_str(db, tkl->ptr.spamfilter->match->str));
W_SAFE(write_str(fd, target)); W_SAFE(unrealdb_write_str(db, target));
W_SAFE(write_data(fd, &action, sizeof(action))); W_SAFE(unrealdb_write_char(db, action));
W_SAFE(write_str(fd, tkl->ptr.spamfilter->tkl_reason)); W_SAFE(unrealdb_write_str(db, tkl->ptr.spamfilter->tkl_reason));
W_SAFE(write_int64(fd, tkl->ptr.spamfilter->tkl_duration)); W_SAFE(unrealdb_write_int64(db, tkl->ptr.spamfilter->tkl_duration));
} }
return 1; return 1;
@ -409,7 +447,7 @@ int write_tkline(FILE *fd, const char *tmpfname, TKL *tkl)
/** Read all entries from the TKL db */ /** Read all entries from the TKL db */
int read_tkldb(void) int read_tkldb(void)
{ {
FILE *fd; UnrealDB *db;
TKL *tkl = NULL; TKL *tkl = NULL;
uint32_t magic = 0; uint32_t magic = 0;
uint32_t version; uint32_t version;
@ -426,49 +464,61 @@ int read_tkldb(void)
gettimeofday(&tv_alpha, NULL); gettimeofday(&tv_alpha, NULL);
#endif #endif
fd = fopen(cfg.database, "rb"); db = unrealdb_open(cfg.database, UNREALDB_MODE_READ, cfg.db_secret);
if (!fd) if (!db)
{ {
if (errno == ENOENT) if (unrealdb_get_error_code() == UNREALDB_ERROR_FILENOTFOUND)
{ {
/* Database does not exist. Could be first boot */ /* Database does not exist. Could be first boot */
config_warn("[tkldb] No database present at '%s', will start a new one", cfg.database); config_warn("[tkldb] No database present at '%s', will start a new one", cfg.database);
return 1; return 1;
} else { } else
config_warn("[tkldb] Unable to open database file '%s' for reading: %s", cfg.database, strerror(errno)); if (unrealdb_get_error_code() == UNREALDB_ERROR_NOTCRYPTED)
{
/* Re-open as unencrypted */
db = unrealdb_open(cfg.database, UNREALDB_MODE_READ, NULL);
if (!db)
{
/* This should actually never happen, unless some weird I/O error */
config_warn("[tkldb] Unable to open the database file '%s': %s", cfg.database, unrealdb_get_error_string());
return 0;
}
} else
{
config_warn("[tkldb] Unable to open the database file '%s' for reading: %s", cfg.database, unrealdb_get_error_string());
return 0; return 0;
} }
} }
/* The database starts with a "magic value" - unless it's some old version or corrupt */ /* The database starts with a "magic value" - unless it's some old version or corrupt */
R_SAFE(read_data(fd, &magic, sizeof(magic))); R_SAFE(unrealdb_read_int32(db, &magic));
if (magic != TKLDB_MAGIC) if (magic != TKLDB_MAGIC)
{ {
config_warn("[tkldb] Database '%s' uses an old and unsupported format OR is corrupt", cfg.database); config_warn("[tkldb] Database '%s' uses an old and unsupported format OR is corrupt", cfg.database);
config_status("If you are upgrading from UnrealIRCd 4 (or 5.0.0-alpha1) then we suggest you to " config_status("If you are upgrading from UnrealIRCd 4 (or 5.0.0-alpha1) then we suggest you to "
"delete the existing database. Just keep at least 1 server linked during the upgrade " "delete the existing database. Just keep at least 1 server linked during the upgrade "
"process to preserve your global *LINES and Spamfilters."); "process to preserve your global *LINES and Spamfilters.");
fclose(fd); unrealdb_close(db);
return 0; return 0;
} }
/* Now do a version check */ /* Now do a version check */
R_SAFE(read_data(fd, &version, sizeof(version))); R_SAFE(unrealdb_read_int32(db, &version));
if (version < 4999) if (version < 4999)
{ {
config_warn("[tkldb] Database '%s' uses an unsupport - possibly old - format (%ld).", cfg.database, (long)version); config_warn("[tkldb] Database '%s' uses an unsupport - possibly old - format (%ld).", cfg.database, (long)version);
fclose(fd); unrealdb_close(db);
return 0; return 0;
} }
if (version > tkldb_version) if (version > tkldb_version)
{ {
config_warn("[tkldb] Database '%s' has version %lu while we only support %lu. Did you just downgrade UnrealIRCd? Sorry this is not suported", config_warn("[tkldb] Database '%s' has version %lu while we only support %lu. Did you just downgrade UnrealIRCd? Sorry this is not suported",
cfg.database, (unsigned long)tkldb_version, (unsigned long)version); cfg.database, (unsigned long)tkldb_version, (unsigned long)version);
fclose(fd); unrealdb_close(db);
return 0; return 0;
} }
R_SAFE(read_data(fd, &tklcount, sizeof(tklcount))); R_SAFE(unrealdb_read_int64(db, &tklcount));
for (cnt = 0; cnt < tklcount; cnt++) for (cnt = 0; cnt < tklcount; cnt++)
{ {
@ -477,7 +527,7 @@ int read_tkldb(void)
tkl = safe_alloc(sizeof(TKL)); tkl = safe_alloc(sizeof(TKL));
/* First, fetch the TKL type.. */ /* First, fetch the TKL type.. */
R_SAFE(read_data(fd, &c, sizeof(c))); R_SAFE(unrealdb_read_char(db, &c));
tkl->type = tkl_chartotype(c); tkl->type = tkl_chartotype(c);
if (!tkl->type) if (!tkl->type)
{ {
@ -493,10 +543,10 @@ int read_tkldb(void)
} }
/* Read the common types (same for all TKLs) */ /* Read the common types (same for all TKLs) */
R_SAFE(read_str(fd, &tkl->set_by)); R_SAFE(unrealdb_read_str(db, &tkl->set_by));
R_SAFE(read_int64(fd, &v)); R_SAFE(unrealdb_read_int64(db, &v));
tkl->set_at = v; tkl->set_at = v;
R_SAFE(read_int64(fd, &v)); R_SAFE(unrealdb_read_int64(db, &v));
tkl->expire_at = v; tkl->expire_at = v;
/* Save some CPU... if it's already expired then don't bother adding */ /* Save some CPU... if it's already expired then don't bother adding */
@ -513,7 +563,7 @@ int read_tkldb(void)
/* Usermask - but taking into account that the /* Usermask - but taking into account that the
* %-prefix means a soft ban. * %-prefix means a soft ban.
*/ */
R_SAFE(read_str(fd, &str)); R_SAFE(unrealdb_read_str(db, &str));
if (*str == '%') if (*str == '%')
{ {
softban = 1; softban = 1;
@ -524,8 +574,8 @@ int read_tkldb(void)
safe_free(str); safe_free(str);
/* And the other 2 fields.. */ /* And the other 2 fields.. */
R_SAFE(read_str(fd, &tkl->ptr.serverban->hostmask)); R_SAFE(unrealdb_read_str(db, &tkl->ptr.serverban->hostmask));
R_SAFE(read_str(fd, &tkl->ptr.serverban->reason)); R_SAFE(unrealdb_read_str(db, &tkl->ptr.serverban->reason));
if (find_tkl_serverban(tkl->type, tkl->ptr.serverban->usermask, if (find_tkl_serverban(tkl->type, tkl->ptr.serverban->usermask,
tkl->ptr.serverban->hostmask, softban)) tkl->ptr.serverban->hostmask, softban))
@ -551,7 +601,7 @@ int read_tkldb(void)
/* Usermask - but taking into account that the /* Usermask - but taking into account that the
* %-prefix means a soft ban. * %-prefix means a soft ban.
*/ */
R_SAFE(read_str(fd, &str)); R_SAFE(unrealdb_read_str(db, &str));
if (*str == '%') if (*str == '%')
{ {
softban = 1; softban = 1;
@ -562,9 +612,9 @@ int read_tkldb(void)
safe_free(str); safe_free(str);
/* And the other 3 fields.. */ /* And the other 3 fields.. */
R_SAFE(read_str(fd, &tkl->ptr.banexception->hostmask)); R_SAFE(unrealdb_read_str(db, &tkl->ptr.banexception->hostmask));
R_SAFE(read_str(fd, &tkl->ptr.banexception->bantypes)); R_SAFE(unrealdb_read_str(db, &tkl->ptr.banexception->bantypes));
R_SAFE(read_str(fd, &tkl->ptr.banexception->reason)); R_SAFE(unrealdb_read_str(db, &tkl->ptr.banexception->reason));
if (find_tkl_banexception(tkl->type, tkl->ptr.banexception->usermask, if (find_tkl_banexception(tkl->type, tkl->ptr.banexception->usermask,
tkl->ptr.banexception->hostmask, softban)) tkl->ptr.banexception->hostmask, softban))
@ -587,12 +637,12 @@ int read_tkldb(void)
{ {
tkl->ptr.nameban = safe_alloc(sizeof(NameBan)); tkl->ptr.nameban = safe_alloc(sizeof(NameBan));
R_SAFE(read_str(fd, &str)); R_SAFE(unrealdb_read_str(db, &str));
if (*str == 'H') if (*str == 'H')
tkl->ptr.nameban->hold = 1; tkl->ptr.nameban->hold = 1;
safe_free(str); safe_free(str);
R_SAFE(read_str(fd, &tkl->ptr.nameban->name)); R_SAFE(unrealdb_read_str(db, &tkl->ptr.nameban->name));
R_SAFE(read_str(fd, &tkl->ptr.nameban->reason)); R_SAFE(unrealdb_read_str(db, &tkl->ptr.nameban->reason));
if (find_tkl_nameban(tkl->type, tkl->ptr.nameban->name, if (find_tkl_nameban(tkl->type, tkl->ptr.nameban->name,
tkl->ptr.nameban->hold)) tkl->ptr.nameban->hold))
@ -617,7 +667,7 @@ int read_tkldb(void)
tkl->ptr.spamfilter = safe_alloc(sizeof(Spamfilter)); tkl->ptr.spamfilter = safe_alloc(sizeof(Spamfilter));
/* Match method */ /* Match method */
R_SAFE(read_str(fd, &str)); R_SAFE(unrealdb_read_str(db, &str));
match_method = unreal_match_method_strtoval(str); match_method = unreal_match_method_strtoval(str);
if (!match_method) if (!match_method)
{ {
@ -627,7 +677,7 @@ int read_tkldb(void)
safe_free(str); safe_free(str);
/* Match string (eg: regex) */ /* Match string (eg: regex) */
R_SAFE(read_str(fd, &str)); R_SAFE(unrealdb_read_str(db, &str));
tkl->ptr.spamfilter->match = unreal_create_match(match_method, str, &err); tkl->ptr.spamfilter->match = unreal_create_match(match_method, str, &err);
if (!tkl->ptr.spamfilter->match) if (!tkl->ptr.spamfilter->match)
{ {
@ -637,7 +687,7 @@ int read_tkldb(void)
safe_free(str); safe_free(str);
/* Target (eg: cpn) */ /* Target (eg: cpn) */
R_SAFE(read_str(fd, &str)); R_SAFE(unrealdb_read_str(db, &str));
tkl->ptr.spamfilter->target = spamfilter_gettargets(str, NULL); tkl->ptr.spamfilter->target = spamfilter_gettargets(str, NULL);
if (!tkl->ptr.spamfilter->target) if (!tkl->ptr.spamfilter->target)
{ {
@ -648,7 +698,7 @@ int read_tkldb(void)
safe_free(str); safe_free(str);
/* Action */ /* Action */
R_SAFE(read_data(fd, &c, sizeof(c))); R_SAFE(unrealdb_read_char(db, &c));
tkl->ptr.spamfilter->action = banact_chartoval(c); tkl->ptr.spamfilter->action = banact_chartoval(c);
if (!tkl->ptr.spamfilter->action) if (!tkl->ptr.spamfilter->action)
{ {
@ -657,8 +707,8 @@ int read_tkldb(void)
do_not_add = 1; do_not_add = 1;
} }
R_SAFE(read_str(fd, &tkl->ptr.spamfilter->tkl_reason)); R_SAFE(unrealdb_read_str(db, &tkl->ptr.spamfilter->tkl_reason));
R_SAFE(read_int64(fd, &v)); R_SAFE(unrealdb_read_int64(db, &v));
tkl->ptr.spamfilter->tkl_duration = v; tkl->ptr.spamfilter->tkl_duration = v;
if (find_tkl_spamfilter(tkl->type, tkl->ptr.spamfilter->match->str, if (find_tkl_spamfilter(tkl->type, tkl->ptr.spamfilter->match->str,
@ -695,10 +745,7 @@ int read_tkldb(void)
FreeTKLRead(); FreeTKLRead();
} }
/* If everything went fine, then reading a single byte should cause an EOF error */ unrealdb_close(db);
if (fread(&c, 1, 1, fd) == 1)
config_warn("[tkldb] Database invalid. Extra data found at end of DB file.");
fclose(fd);
if (added_cnt) if (added_cnt)
sendto_realops_and_log("[tkldb] Re-added %d *-Lines", added_cnt); sendto_realops_and_log("[tkldb] Re-added %d *-Lines", added_cnt);

View File

@ -242,7 +242,7 @@ CMD_FUNC(cmd_topic)
Hook *tmphook; Hook *tmphook;
int n; int n;
if (match_spamfilter(client, topic, SPAMF_TOPIC, channel->chname, 0, NULL)) if (match_spamfilter(client, topic, SPAMF_TOPIC, "TOPIC", channel->chname, 0, NULL))
return; return;
for (tmphook = Hooks[HOOKTYPE_PRE_LOCAL_TOPIC]; tmphook; tmphook = tmphook->next) { for (tmphook = Hooks[HOOKTYPE_PRE_LOCAL_TOPIC]; tmphook; tmphook = tmphook->next) {

View File

@ -26,6 +26,11 @@ ModuleHeader MOD_HEADER
#define WEBSOCKET_SEND_BUFFER_SIZE 16384 #define WEBSOCKET_SEND_BUFFER_SIZE 16384
#endif #endif
typedef enum WebSocketType {
WEBSOCKET_TYPE_BINARY = 1,
WEBSOCKET_TYPE_TEXT = 2
} WebSocketType;
typedef struct WebSocketUser WebSocketUser; typedef struct WebSocketUser WebSocketUser;
struct WebSocketUser { struct WebSocketUser {
char get; /**< GET initiated */ char get; /**< GET initiated */
@ -33,14 +38,14 @@ struct WebSocketUser {
char *handshake_key; /**< Handshake key (used during handshake) */ char *handshake_key; /**< Handshake key (used during handshake) */
char *lefttoparse; /**< Leftover buffer to parse */ char *lefttoparse; /**< Leftover buffer to parse */
int lefttoparselen; /**< Length of lefttoparse buffer */ int lefttoparselen; /**< Length of lefttoparse buffer */
WebSocketType type; /**< WEBSOCKET_TYPE_BINARY or WEBSOCKET_TYPE_TEXT */
char *sec_websocket_protocol; /**< Only valid during parsing of the request, after that it is NULL again */
}; };
#define WEBSOCKET_TYPE_BINARY 0x1
#define WEBSOCKET_TYPE_TEXT 0x2
#define WSU(client) ((WebSocketUser *)moddata_client(client, websocket_md).ptr) #define WSU(client) ((WebSocketUser *)moddata_client(client, websocket_md).ptr)
#define WEBSOCKET_TYPE(client) ((client->local && client->local->listener) ? client->local->listener->websocket_options : 0) #define WEBSOCKET_PORT(client) ((client->local && client->local->listener) ? client->local->listener->websocket_options : 0)
#define WEBSOCKET_TYPE(client) (WSU(client)->type)
#define WEBSOCKET_MAGIC_KEY "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" /* see RFC6455 */ #define WEBSOCKET_MAGIC_KEY "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" /* see RFC6455 */
@ -59,7 +64,7 @@ int websocket_packet_in(Client *client, char *readbuf, int *length);
void websocket_mdata_free(ModData *m); void websocket_mdata_free(ModData *m);
int websocket_handle_packet(Client *client, char *readbuf, int length); int websocket_handle_packet(Client *client, char *readbuf, int length);
int websocket_handle_handshake(Client *client, char *readbuf, int *length); int websocket_handle_handshake(Client *client, char *readbuf, int *length);
int websocket_complete_handshake(Client *client); int websocket_handshake_send_response(Client *client);
int websocket_handle_packet_ping(Client *client, char *buf, int len); int websocket_handle_packet_ping(Client *client, char *buf, int len);
int websocket_handle_packet_pong(Client *client, char *buf, int len); int websocket_handle_packet_pong(Client *client, char *buf, int len);
int websocket_create_packet(int opcode, char **buf, int *len); int websocket_create_packet(int opcode, char **buf, int *len);
@ -67,6 +72,7 @@ int websocket_send_pong(Client *client, char *buf, int len);
/* Global variables */ /* Global variables */
ModDataInfo *websocket_md; ModDataInfo *websocket_md;
static int ws_text_mode_available = 1;
MOD_TEST() MOD_TEST()
{ {
@ -98,6 +104,8 @@ MOD_INIT()
MOD_LOAD() MOD_LOAD()
{ {
if (non_utf8_nick_chars_in_use || (iConf.allowed_channelchars == ALLOWED_CHANNELCHARS_ANY))
ws_text_mode_available = 0;
return MOD_SUCCESS; return MOD_SUCCESS;
} }
@ -302,11 +310,12 @@ int websocket_handle_websocket(Client *client, char *readbuf2, int length2)
*/ */
int websocket_packet_in(Client *client, char *readbuf, int *length) int websocket_packet_in(Client *client, char *readbuf, int *length)
{ {
if ((client->local->receiveM == 0) && WEBSOCKET_TYPE(client) && !WSU(client) && (*length > 8) && !strncmp(readbuf, "GET ", 4)) if ((client->local->receiveM == 0) && WEBSOCKET_PORT(client) && !WSU(client) && (*length > 8) && !strncmp(readbuf, "GET ", 4))
{ {
/* Allocate a new WebSocketUser struct for this session */ /* Allocate a new WebSocketUser struct for this session */
moddata_client(client, websocket_md).ptr = safe_alloc(sizeof(WebSocketUser)); moddata_client(client, websocket_md).ptr = safe_alloc(sizeof(WebSocketUser));
WSU(client)->get = 1; WSU(client)->get = 1;
WSU(client)->type = client->local->listener->websocket_options; /* the default, unless the client chooses otherwise */
} }
if (!WSU(client)) if (!WSU(client))
@ -452,6 +461,58 @@ int websocket_handshake_helper(char *buffer, int len, char **key, char **value,
return 0; return 0;
} }
/** Finally, validate the websocket request (handshake) and proceed or reject. */
int websocket_handshake_valid(Client *client)
{
if (!WSU(client)->handshake_key)
{
if (is_module_loaded("webredir"))
{
char *parx[2] = { NULL, NULL };
do_cmd(client, NULL, "GET", 1, parx);
}
dead_socket(client, "Invalid WebSocket request");
return 0;
}
if (WSU(client)->sec_websocket_protocol)
{
char *p = NULL, *name;
int negotiated = 0;
for (name = strtoken(&p, WSU(client)->sec_websocket_protocol, ",");
name;
name = strtoken(&p, NULL, ","))
{
skip_whitespace(&name);
if (!strcmp(name, "binary.ircv3.net"))
{
negotiated = WEBSOCKET_TYPE_BINARY;
break; /* First hit wins */
} else
if (!strcmp(name, "text.ircv3.net") && ws_text_mode_available)
{
negotiated = WEBSOCKET_TYPE_TEXT;
break; /* First hit wins */
}
}
if (negotiated == WEBSOCKET_TYPE_BINARY)
{
WSU(client)->type = WEBSOCKET_TYPE_BINARY;
safe_strdup(WSU(client)->sec_websocket_protocol, "binary.ircv3.net");
} else
if (negotiated == WEBSOCKET_TYPE_TEXT)
{
WSU(client)->type = WEBSOCKET_TYPE_TEXT;
safe_strdup(WSU(client)->sec_websocket_protocol, "text.ircv3.net");
} else
{
/* Negotiation failed, fallback to the default (don't set it here) */
safe_free(WSU(client)->sec_websocket_protocol);
}
}
return 1;
}
/** Handle client GET WebSocket handshake. /** Handle client GET WebSocket handshake.
* Yes, I'm going to assume that the header fits in one packet and one packet only. * Yes, I'm going to assume that the header fits in one packet and one packet only.
*/ */
@ -500,22 +561,19 @@ int websocket_handle_handshake(Client *client, char *readbuf, int *length)
return -1; return -1;
} }
safe_strdup(WSU(client)->handshake_key, value); safe_strdup(WSU(client)->handshake_key, value);
} else
if (!strcasecmp(key, "Sec-WebSocket-Protocol"))
{
/* Save it here, will be processed later */
safe_strdup(WSU(client)->sec_websocket_protocol, value);
} }
} }
if (end_of_request) if (end_of_request)
{ {
if (!WSU(client)->handshake_key) if (!websocket_handshake_valid(client))
{
if (is_module_loaded("webredir"))
{
char *parx[2] = { NULL, NULL };
do_cmd(client, NULL, "GET", 1, parx);
}
dead_socket(client, "Invalid WebSocket request");
return -1; return -1;
} websocket_handshake_send_response(client);
websocket_complete_handshake(client);
return 0; return 0;
} }
@ -528,7 +586,7 @@ int websocket_handle_handshake(Client *client, char *readbuf, int *length)
} }
/** Complete the handshake by sending the appropriate HTTP 101 response etc. */ /** Complete the handshake by sending the appropriate HTTP 101 response etc. */
int websocket_complete_handshake(Client *client) int websocket_handshake_send_response(Client *client)
{ {
char buf[512], hashbuf[64]; char buf[512], hashbuf[64];
SHA_CTX hash; SHA_CTX hash;
@ -547,10 +605,21 @@ int websocket_complete_handshake(Client *client)
"HTTP/1.1 101 Switching Protocols\r\n" "HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n" "Upgrade: websocket\r\n"
"Connection: Upgrade\r\n" "Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: %s\r\n" "Sec-WebSocket-Accept: %s\r\n",
"\r\n",
hashbuf); hashbuf);
if (WSU(client)->sec_websocket_protocol)
{
/* using strlen() is safe here since above buffer will not
* cause it to be >=512 and thus we won't get into negatives.
*/
snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf),
"Sec-WebSocket-Protocol: %s\r\n",
WSU(client)->sec_websocket_protocol);
}
strlcat(buf, "\r\n", sizeof(buf));
/* Caution: we bypass sendQ flood checking by doing it this way. /* Caution: we bypass sendQ flood checking by doing it this way.
* Risk is minimal, though, as we only permit limited text only * Risk is minimal, though, as we only permit limited text only
* once per session. * once per session.

View File

@ -103,15 +103,8 @@ CMD_FUNC(cmd_whois)
if (wilds) if (wilds)
continue; continue;
if ((target = find_client(nick, NULL))) if ((target = find_person(nick, NULL)))
{ {
if (IsServer(target))
continue;
/*
* I'm always last :-) and target->next == NULL!!
*/
if (IsMe(target))
break;
/* /*
* 'Rules' established for sending a WHOIS reply: * 'Rules' established for sending a WHOIS reply:
* - only send replies about common or public channels * - only send replies about common or public channels

View File

@ -35,6 +35,7 @@ ModuleHeader MOD_HEADER
#define FIELD_OPLEVEL 0x1000 /* meaningless and stupid, but whatever */ #define FIELD_OPLEVEL 0x1000 /* meaningless and stupid, but whatever */
#define FIELD_REALHOST 0x2000 #define FIELD_REALHOST 0x2000
#define FIELD_MODES 0x4000 #define FIELD_MODES 0x4000
#define FIELD_REPUTATION 0x8000
#define WMATCH_NICK 0x0001 #define WMATCH_NICK 0x0001
#define WMATCH_USER 0x0002 #define WMATCH_USER 0x0002
@ -261,6 +262,7 @@ CMD_FUNC(cmd_whox)
case 'a': fmt.fields |= FIELD_ACCOUNT; break; case 'a': fmt.fields |= FIELD_ACCOUNT; break;
case 'm': fmt.fields |= FIELD_MODES; break; case 'm': fmt.fields |= FIELD_MODES; break;
case 'o': fmt.fields |= FIELD_OPLEVEL; break; case 'o': fmt.fields |= FIELD_OPLEVEL; break;
case 'R': fmt.fields |= FIELD_REPUTATION; break;
case ',': case ',':
s++; s++;
fmt.querytype = s; fmt.querytype = s;
@ -814,6 +816,13 @@ static void do_who(Client *client, Client *acptr, Channel *channel, struct who_f
append_format(str, sizeof str, &pos, " %s", (!isdigit(*acptr->user->svid)) ? acptr->user->svid : "0"); append_format(str, sizeof str, &pos, " %s", (!isdigit(*acptr->user->svid)) ? acptr->user->svid : "0");
if (HasField(fmt, FIELD_OPLEVEL)) if (HasField(fmt, FIELD_OPLEVEL))
append_format(str, sizeof str, &pos, " %s", (channel && is_skochanop(acptr, channel)) ? "999" : "n/a"); append_format(str, sizeof str, &pos, " %s", (channel && is_skochanop(acptr, channel)) ? "999" : "n/a");
if (HasField(fmt, FIELD_REPUTATION))
{
if (IsOper(client))
append_format(str, sizeof str, &pos, " %d", GetReputation(acptr));
else
append_format(str, sizeof str, &pos, " %s", "*");
}
if (HasField(fmt, FIELD_INFO)) if (HasField(fmt, FIELD_INFO))
append_format(str, sizeof str, &pos, " :%s", acptr->info); append_format(str, sizeof str, &pos, " :%s", acptr->info);

View File

@ -478,7 +478,7 @@ static char *replies[] = {
/* 435 */ NULL, /* bahamut */ /* 435 */ NULL, /* bahamut */
/* 436 ERR_NICKCOLLISION */ "%s :Nickname collision KILL", /* 436 ERR_NICKCOLLISION */ "%s :Nickname collision KILL",
/* 437 ERR_BANNICKCHANGE */ "%s :Cannot change nickname while banned on channel", /* 437 ERR_BANNICKCHANGE */ "%s :Cannot change nickname while banned on channel",
/* 438 ERR_NCHANGETOOFAST */ "%s :Nick change too fast. Please wait %d seconds", /* 438 ERR_NCHANGETOOFAST */ "%s :Nick change too fast. Please try again later.",
/* 439 ERR_TARGETTOOFAST */ "%s :Message target change too fast. Please wait %ld seconds", /* 439 ERR_TARGETTOOFAST */ "%s :Message target change too fast. Please wait %ld seconds",
/* 440 ERR_SERVICESDOWN */ "%s :Services are currently down. Please try again later.", /* 440 ERR_SERVICESDOWN */ "%s :Services are currently down. Please try again later.",
/* 441 ERR_USERNOTINCHANNEL */ "%s %s :They aren't on that channel", /* 441 ERR_USERNOTINCHANNEL */ "%s %s :They aren't on that channel",

View File

@ -485,6 +485,11 @@ static void parse2(Client *cptr, Client **fromptr, MessageTag *mtags, char *ch)
} }
} }
para[++i] = NULL; para[++i] = NULL;
/* Check if one of the message tags are rejected by spamfilter */
if (MyConnect(from) && !IsServer(from) && match_spamfilter_mtags(from, mtags, cmptr ? cmptr->cmd : NULL))
return;
if (cmptr == NULL) if (cmptr == NULL)
{ {
do_numeric(numeric, from, mtags, i, para); do_numeric(numeric, from, mtags, i, para);
@ -494,6 +499,7 @@ static void parse2(Client *cptr, Client **fromptr, MessageTag *mtags, char *ch)
if (IsUser(cptr) && (cmptr->flags & CMD_RESETIDLE)) if (IsUser(cptr) && (cmptr->flags & CMD_RESETIDLE))
cptr->local->last = TStime(); cptr->local->last = TStime();
/* Now ready to execute the command */
#ifndef DEBUGMODE #ifndef DEBUGMODE
if (cmptr->flags & CMD_ALIAS) if (cmptr->flags & CMD_ALIAS)
{ {

View File

@ -7,7 +7,7 @@
#include "unrealircd.h" #include "unrealircd.h"
/* /*
* ircd used to store full servernames in ClientUser as well as in the * ircd used to store full servernames in User as well as in the
* whowas info. there can be some 40k such structures alive at any * whowas info. there can be some 40k such structures alive at any
* given time, while the number of unique server names a server sees in * given time, while the number of unique server names a server sees in
* its lifetime is at most a few hundred. by tokenizing server names * its lifetime is at most a few hundred. by tokenizing server names

View File

@ -28,6 +28,7 @@
/* s_serv.c 2.55 2/7/94 (C) 1988 University of Oulu, Computing Center and Jarkko Oikarinen */ /* s_serv.c 2.55 2/7/94 (C) 1988 University of Oulu, Computing Center and Jarkko Oikarinen */
#include "unrealircd.h" #include "unrealircd.h"
#include <ares.h>
#ifndef _WIN32 #ifndef _WIN32
/* for uname(), is POSIX so should be OK... */ /* for uname(), is POSIX so should be OK... */
#include <sys/utsname.h> #include <sys/utsname.h>
@ -176,10 +177,12 @@ CMD_FUNC(cmd_version)
if (ValidatePermissionsForPath("server:info",client,NULL,NULL,NULL)) if (ValidatePermissionsForPath("server:info",client,NULL,NULL,NULL))
{ {
sendnotice(client, "%s", SSLeay_version(SSLEAY_VERSION)); sendnotice(client, "%s", SSLeay_version(SSLEAY_VERSION));
sendnotice(client, "%s", pcre2_version()); sendnotice(client, "libsodium %s", sodium_version_string());
#ifdef USE_LIBCURL #ifdef USE_LIBCURL
sendnotice(client, "%s", curl_version()); sendnotice(client, "%s", curl_version());
#endif #endif
sendnotice(client, "c-ares %s", ares_version(NULL));
sendnotice(client, "%s", pcre2_version());
} }
if (MyUser(client)) if (MyUser(client))
send_version(client,RPL_ISUPPORT); send_version(client,RPL_ISUPPORT);

View File

@ -724,6 +724,34 @@ void outofmemory(size_t bytes)
exit(7); exit(7);
} }
/** Allocate sensitive memory - this should only be used for HIGHLY sensitive data, since
* it wastes 8192+ bytes even if only asked to allocate for example 32 bytes (this is by design).
* @param size How many bytes to allocate
* @returns A pointer to the newly allocated memory.
* @note If out of memory then the IRCd will exit.
*/
void *safe_alloc_sensitive(size_t size)
{
void *p;
if (size == 0)
return NULL;
p = sodium_malloc(((size/32)*32)+32);
if (!p)
outofmemory(size);
memset(p, 0, size);
return p;
}
/** Safely duplicate a string */
char *our_strdup_sensitive(const char *str)
{
char *ret = safe_alloc_sensitive(strlen(str)+1);
if (!ret)
outofmemory(strlen(str));
strcpy(ret, str); /* safe, see above */
return ret;
}
/** Returns a unique filename in the specified directory /** Returns a unique filename in the specified directory
* using the specified suffix. The returned value will * using the specified suffix. The returned value will
* be of the form <dir>/<random-hex>.<suffix> * be of the form <dir>/<random-hex>.<suffix>
@ -1307,3 +1335,18 @@ char *unreal_strftime(char *str)
return str; return str;
return buf; return buf;
} }
/** Convert a string to lowercase */
void strtolower_safe(char *dst, char *src, int size)
{
if (!size)
return; /* size of 0 is unworkable */
size--; /* for \0 */
for (; *src && size; src++)
{
*dst++ = tolower(*src);
size--;
}
*dst = '\0';
}

1163
src/unrealdb.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -142,6 +142,8 @@ int target_limit_exceeded(Client *client, void *target, const char *name)
{ {
u_char hash = hash_target(target); u_char hash = hash_target(target);
int i; int i;
int max_concurrent_conversations_users, max_concurrent_conversations_new_user_every;
FloodSettings *settings;
if (ValidatePermissionsForPath("immune:max-concurrent-conversations",client,NULL,NULL,NULL)) if (ValidatePermissionsForPath("immune:max-concurrent-conversations",client,NULL,NULL,NULL))
return 0; return 0;
@ -149,7 +151,18 @@ int target_limit_exceeded(Client *client, void *target, const char *name)
if (client->local->targets[0] == hash) if (client->local->targets[0] == hash)
return 0; return 0;
for (i = 1; i < iConf.max_concurrent_conversations_users; i++) settings = get_floodsettings_for_user(client, FLD_CONVERSATIONS);
max_concurrent_conversations_users = settings->limit[FLD_CONVERSATIONS];
max_concurrent_conversations_new_user_every = settings->period[FLD_CONVERSATIONS];
if (max_concurrent_conversations_users <= 0)
return 0; /* unlimited */
/* Shouldn't be needed, but better check here than access out-of-bounds memory */
if (max_concurrent_conversations_users > MAXCCUSERS)
max_concurrent_conversations_users = MAXCCUSERS;
for (i = 1; i < max_concurrent_conversations_users; i++)
{ {
if (client->local->targets[i] == hash) if (client->local->targets[i] == hash)
{ {
@ -166,6 +179,7 @@ int target_limit_exceeded(Client *client, void *target, const char *name)
client->local->nexttarget += 2; /* punish them some more */ client->local->nexttarget += 2; /* punish them some more */
client->local->since += 2; /* lag them up as well */ client->local->since += 2; /* lag them up as well */
flood_limit_exceeded_log(client, "max-concurrent-conversations");
sendnumeric(client, ERR_TARGETTOOFAST, name, client->local->nexttarget - TStime()); sendnumeric(client, ERR_TARGETTOOFAST, name, client->local->nexttarget - TStime());
return 1; return 1;
@ -175,15 +189,15 @@ int target_limit_exceeded(Client *client, void *target, const char *name)
* This is so client->local->nexttarget=0 will become client->local->nexttarget=currenttime-... * This is so client->local->nexttarget=0 will become client->local->nexttarget=currenttime-...
*/ */
if (TStime() > client->local->nexttarget + if (TStime() > client->local->nexttarget +
(iConf.max_concurrent_conversations_users * iConf.max_concurrent_conversations_new_user_every)) (max_concurrent_conversations_users * max_concurrent_conversations_new_user_every))
{ {
client->local->nexttarget = TStime() - ((iConf.max_concurrent_conversations_users-1) * iConf.max_concurrent_conversations_new_user_every); client->local->nexttarget = TStime() - ((max_concurrent_conversations_users-1) * max_concurrent_conversations_new_user_every);
} }
client->local->nexttarget += iConf.max_concurrent_conversations_new_user_every; client->local->nexttarget += max_concurrent_conversations_new_user_every;
/* Add the new target (first move the rest, then add us at position 0 */ /* Add the new target (first move the rest, then add us at position 0 */
memmove(&client->local->targets[1], &client->local->targets[0], iConf.max_concurrent_conversations_users - 1); memmove(&client->local->targets[1], &client->local->targets[0], max_concurrent_conversations_users - 1);
client->local->targets[0] = hash; client->local->targets[0] = hash;
return 0; return 0;
@ -698,6 +712,12 @@ void make_cloakedhost(Client *client, char *curr, char *buf, size_t buflen)
/** Called after a user is logged in (or out) of a services account */ /** Called after a user is logged in (or out) of a services account */
void user_account_login(MessageTag *recv_mtags, Client *client) void user_account_login(MessageTag *recv_mtags, Client *client)
{ {
if (MyConnect(client))
{
find_shun(client);
if (find_tkline_match(client, 0) && IsDead(client))
return;
}
RunHook2(HOOKTYPE_ACCOUNT_LOGIN, client, recv_mtags); RunHook2(HOOKTYPE_ACCOUNT_LOGIN, client, recv_mtags);
} }
@ -811,6 +831,10 @@ void set_security_group_defaults(void)
} }
securitygroups = NULL; securitygroups = NULL;
/* Default group: webirc */
s = add_security_group("webirc-users", 50);
s->webirc = 1;
/* Default group: known-users */ /* Default group: known-users */
s = add_security_group("known-users", 100); s = add_security_group("known-users", 100);
s->identified = 1; s->identified = 1;
@ -919,3 +943,110 @@ char *get_connect_extinfo(Client *client)
return retbuf; return retbuf;
} }
/** Log a message that flood protection kicked in for the client.
* This sends to the +f snomask at the moment.
* @param client The client to check flood for (local user)
* @param opt The flood option (eg FLD_AWAY)
*/
void flood_limit_exceeded_log(Client *client, char *floodname)
{
char buf[1024];
snprintf(buf, sizeof(buf), "Flood blocked (%s) from %s!%s@%s [%s]",
floodname,
client->name,
client->user->username,
client->user->realhost,
GetIP(client));
ircd_log(LOG_FLOOD, "%s", buf);
sendto_snomask_global(SNO_FLOOD, "%s", buf);
}
/** Is the flood limit exceeded for an option? eg for away-flood.
* @param client The client to check flood for (local user)
* @param opt The flood option (eg FLD_AWAY)
* @note This increments the flood counter as well.
* @returns 1 if exceeded, 0 if not.
*/
int flood_limit_exceeded(Client *client, FloodOption opt)
{
FloodSettings *f;
if (!MyUser(client))
return 0;
f = get_floodsettings_for_user(client, opt);
if (f->limit[opt] <= 0)
return 0; /* No limit set or unlimited */
ircd_log(LOG_ERROR, "Checking flood_limit_exceeded() for '%s', type %d with max %d:%ld...",
client->name, (int)opt, (int)f->limit[opt], (long)f->period[opt]);
/* Ok, let's do the flood check */
if ((client->local->flood[opt].t + f->period[opt]) <= timeofday)
{
/* Time exceeded, reset */
client->local->flood[opt].count = 0;
client->local->flood[opt].t = timeofday;
}
if (client->local->flood[opt].count <= f->limit[opt])
client->local->flood[opt].count++;
if (client->local->flood[opt].count > f->limit[opt])
{
flood_limit_exceeded_log(client, floodoption_names[opt]);
return 1; /* Flood limit hit! */
}
return 0;
}
/** Get the appropriate anti-flood settings block for this user.
* @param client The client, should be locally connected.
* @param opt The flood option we are interested in
* @returns The FloodSettings for this user, never returns NULL.
*/
FloodSettings *get_floodsettings_for_user(Client *client, FloodOption opt)
{
SecurityGroup *s;
FloodSettings *f;
/* Go through all security groups by order of priority
* (eg: first "known-users", then "unknown-users").
* For each of these:
* - Check if a set::anti-flood::xxxx block exists for this group
* - Check if the limit is non-zero (eg there is any limit set)
* If any of these are false then we continue with next block
* that matches.
*/
// XXX: alternatively, instead of this double loop,
// do a post-conf thing and sort iConf.floodsettings
// according to the security-group { } order.
for (s = securitygroups; s; s = s->next)
{
if (user_allowed_by_security_group(client, s) &&
((f = find_floodsettings_block(s->name))) &&
f->limit[opt])
{
return f;
}
}
/* Return default settings block (which may have a zero limit set) */
f = find_floodsettings_block("unknown-users");
if (!f)
abort(); /* impossible */
return f;
}
MODVAR char *floodoption_names[] = {
"nick-flood",
"join-flood",
"away-flood",
"invite-flood",
"knock-flood",
"max-concurrent-conversations",
NULL
};

View File

@ -4,7 +4,7 @@ echo "Extracting src/version.c..."
#id=`grep '$Id: Changes,v' ../Changes` #id=`grep '$Id: Changes,v' ../Changes`
#id=`echo $id |sed 's/.* Changes\,v \(.*\) .* Exp .*/\1/'` #id=`echo $id |sed 's/.* Changes\,v \(.*\) .* Exp .*/\1/'`
id="5.0.9" id="5.2.0.1"
echo "$id" echo "$id"
if test -r version.c if test -r version.c
@ -133,7 +133,8 @@ char *unrealcredits[] =
"Underfloor Heating Systems - www.underfloorheatingsystems.co.uk,", "Underfloor Heating Systems - www.underfloorheatingsystems.co.uk,",
"Project Free TV - newprojectfreetv.com, companyaccountscheck.com,", "Project Free TV - newprojectfreetv.com, companyaccountscheck.com,",
"Coyote Traffic - www.coyotetraffic.com, www.emailextractor14lite.com,", "Coyote Traffic - www.coyotetraffic.com, www.emailextractor14lite.com,",
"bet365 Bonus Code - williamspromocodes.co.uk/bet365-bonus-code-sports/", "bet365 Bonus Code - williamspromocodes.co.uk/bet365-bonus-code-sports/,",
"Valerie Amelia Pond",
" ", " ",
"==========================[ Supporters ]==========================", "==========================[ Supporters ]==========================",
"Our support staff: Cronus, Jobe, SpaceDog, Stealth", "Our support staff: Cronus, Jobe, SpaceDog, Stealth",

View File

@ -3,7 +3,7 @@
<assemblyIdentity <assemblyIdentity
processorArchitecture="amd64" processorArchitecture="amd64"
name="UnrealIRCd.UnrealIRCd.5" name="UnrealIRCd.UnrealIRCd.5"
version="5.0.9.0" version="5.2.0.1"
type="win32" type="win32"
/> />
<description>Internet Relay Chat Daemon</description> <description>Internet Relay Chat Daemon</description>

View File

@ -6,7 +6,7 @@
[Setup] [Setup]
AppName=UnrealIRCd 5 AppName=UnrealIRCd 5
AppVerName=UnrealIRCd 5.0.9 AppVerName=UnrealIRCd 5.2.0.1
AppPublisher=UnrealIRCd Team AppPublisher=UnrealIRCd Team
AppPublisherURL=https://www.unrealircd.org AppPublisherURL=https://www.unrealircd.org
AppSupportURL=https://www.unrealircd.org AppSupportURL=https://www.unrealircd.org
@ -72,6 +72,7 @@ Source: "src\modules\third\*.dll"; DestDir: "{app}\modules\third"; Flags: ignore
Source: "c:\dev\unrealircd-5-libs\pcre2\bin\pcre*.dll"; DestDir: "{app}\bin"; Flags: ignoreversion Source: "c:\dev\unrealircd-5-libs\pcre2\bin\pcre*.dll"; DestDir: "{app}\bin"; Flags: ignoreversion
Source: "c:\dev\unrealircd-5-libs\argon2\vs2015\build\*.dll"; DestDir: "{app}\bin"; Flags: ignoreversion Source: "c:\dev\unrealircd-5-libs\argon2\vs2015\build\*.dll"; DestDir: "{app}\bin"; Flags: ignoreversion
Source: "c:\dev\unrealircd-5-libs\libsodium\bin\x64\Release\v142\dynamic\*.dll"; DestDir: "{app}\bin"; Flags: ignoreversion
Source: "c:\dev\unrealircd-5-libs\c-ares\msvc\cares\dll-release\cares.dll"; DestDir: "{app}\bin"; Flags: ignoreversion Source: "c:\dev\unrealircd-5-libs\c-ares\msvc\cares\dll-release\cares.dll"; DestDir: "{app}\bin"; Flags: ignoreversion
Source: "c:\dev\unrealircd-5-libs\libressl\bin\openssl.exe"; DestDir: "{app}\bin"; Flags: ignoreversion Source: "c:\dev\unrealircd-5-libs\libressl\bin\openssl.exe"; DestDir: "{app}\bin"; Flags: ignoreversion
Source: "c:\dev\unrealircd-5-libs\libressl\bin\*.dll"; DestDir: "{app}\bin"; Flags: ignoreversion Source: "c:\dev\unrealircd-5-libs\libressl\bin\*.dll"; DestDir: "{app}\bin"; Flags: ignoreversion

View File

@ -53,17 +53,34 @@ if [ "$1" = "start" ] ; then
# Now check if we need to create a crash report. # Now check if we need to create a crash report.
@BINDIR@/unrealircd -R @BINDIR@/unrealircd -R
elif [ "$1" = "stop" ] ; then elif [ "$1" = "stop" ] ; then
echo "Stopping UnrealIRCd" echo -n "Stopping UnrealIRCd"
if [ ! -r $PID_FILE ] ; then if [ ! -r $PID_FILE ] ; then
echo
echo "ERROR: UnrealIRCd is not running" echo "ERROR: UnrealIRCd is not running"
exit 1 exit 1
fi fi
kill -15 `cat $PID_FILE` kill -15 `cat $PID_FILE`
if [ "$?" != 0 ]; then if [ "$?" != 0 ]; then
echo
echo "ERROR: UnrealIRCd is not running" echo "ERROR: UnrealIRCd is not running"
exit 1 exit 1
fi fi
sleep 1 # Wait for UnrealIRCd to terminate, but wait 10 seconds max
n="0"
while [ "$n" -lt 10 ]
do
echo -n "."
if [ ! -r $PID_FILE ] ; then
break
fi
if ! kill -0 `cat $PID_FILE`; then
break
fi
n=`expr $n + 1`
sleep 1
done
echo
# In case it is still running, kill it for good.
if [ -r $PID_FILE ] ; then if [ -r $PID_FILE ] ; then
kill -9 `cat $PID_FILE` 1>/dev/null 2>&1 kill -9 `cat $PID_FILE` 1>/dev/null 2>&1
fi fi
@ -80,17 +97,7 @@ elif [ "$1" = "rehash" ] ; then
fi fi
elif [ "$1" = "restart" ] ; then elif [ "$1" = "restart" ] ; then
echo "Restarting UnrealIRCd" echo "Restarting UnrealIRCd"
if [ ! -r $PID_FILE ] ; then $0 stop
echo "WARNING: UnrealIRCd was not running"
else
kill -15 `cat $PID_FILE`
if [ "$?" != 0 ]; then
echo "WARNING: UnrealIRCd was not running"
else
sleep 1
kill -9 `cat $PID_FILE` 1>/dev/null 2>&1
fi
fi
$0 start $0 start
elif [ "$1" = "croncheck" ] ; then elif [ "$1" = "croncheck" ] ; then
if [ -r $PID_FILE ] ; then if [ -r $PID_FILE ] ; then
@ -212,7 +219,7 @@ __EOF__
echo '2. If you are using 3rd party modules we might request you' echo '2. If you are using 3rd party modules we might request you'
echo ' to run without them and verify you still crash. This is' echo ' to run without them and verify you still crash. This is'
echo ' to eleminate any loss of time due to bugs made by others' echo ' to eleminate any loss of time due to bugs made by others'
echo '3. Always use the latest UnrealIRCd version, we fix (crash)bugs' echo '3. Use a reasonably recent UnrealIRCd version. We fix (crash)bugs'
echo ' all the time so your bug might as well be fixed already.' echo ' all the time so your bug might as well be fixed already.'
echo "" echo ""
echo "Thanks!" echo "Thanks!"