diff --git a/.gitignore b/.gitignore index 46780c6..2a869a3 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ extras/c-ares* config.status extras/ircdcron/ircd.cron extras/ircdcron/ircdchk -src/modules/snomasks/Makefile src/modules/chanmodes/Makefile src/modules/extbans/Makefile src/modules/usermodes/Makefile diff --git a/Config b/Config index 8625041..3d58067 100755 --- a/Config +++ b/Config @@ -18,7 +18,24 @@ # some bits edited by baafie on March 17 2004, every change marked. +# Remove trailing slash in paths (if any) +FIX_PATHNAMES () { + BASEPATH="${BASEPATH%/}" + BINDIR="${BINDIR%/}" + DATADIR="${DATADIR%/}" + CONFDIR="${CONFDIR%/}" + MODULESDIR="${MODULESDIR%/}" + LOGDIR="${LOGDIR%/}" + CACHEDIR="${CACHEDIR%/}" + DOCDIR="${DOCDIR%/}" + TMPDIR="${TMPDIR%/}" + PRIVATELIBDIR="${PRIVATELIBDIR%/}" + SSLDIR="${SSLDIR%/}" + CURLDIR="${CURLDIR%/}" +} +# Create and run the ./configure command with the appropriate +# options based on the users settings. RUN_CONFIGURE () { ARG=" " @@ -72,6 +89,7 @@ fi ARG="$ARG--with-bindir=$BINDIR " ARG="$ARG--with-datadir=$DATADIR " ARG="$ARG--with-pidfile=$DATADIR/unrealircd.pid " +ARG="$ARG--with-controlfile=$DATADIR/unrealircd.ctl " ARG="$ARG--with-confdir=$CONFDIR " ARG="$ARG--with-modulesdir=$MODULESDIR " ARG="$ARG--with-logdir=$LOGDIR " @@ -292,6 +310,7 @@ while [ $# -ge 1 ] ; do if [ -f "config.settings" ] ; then . ./config.settings fi + FIX_PATHNAMES RUN_CONFIGURE cd "$UNREALCWD" exit 0 @@ -356,7 +375,7 @@ echo "We will now ask you a number of questions. You can just press ENTER to acc echo "" # This needs to be updated each release so auto-upgrading works for settings, modules, etc!!: -UNREALRELEASES="unrealircd-6.0.1 unrealircd-6.0.0 unrealircd-6.0.0-rc2 unrealircd-6.0.0-rc1 unrealircd-6.0.0-beta4 unrealircd-6.0.0-beta3 unrealircd-6.0.0-beta2 unrealircd-6.0.0-beta1 unrealircd-5.2.3 unrealircd-5.2.2 unrealircd-5.2.1.1 unrealircd-5.2.1 unrealircd-5.2.1-rc1 unrealircd-5.2.0.2 unrealircd-5.2.0.1 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" +UNREALRELEASES="unrealircd-6.0.2 unrealircd-6.0.1.1 unrealircd-6.0.1 unrealircd-6.0.0 unrealircd-6.0.0-rc2 unrealircd-6.0.0-rc1 unrealircd-6.0.0-beta4 unrealircd-6.0.0-beta3 unrealircd-6.0.0-beta2 unrealircd-6.0.0-beta1 unrealircd-5.2.3 unrealircd-5.2.2 unrealircd-5.2.1.1 unrealircd-5.2.1 unrealircd-5.2.1-rc1 unrealircd-5.2.0.2 unrealircd-5.2.0.1 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" if [ -f "config.settings" ]; then . ./config.settings else @@ -701,11 +720,12 @@ while [ -z "$TEST" ] ; do TEST="$GEOIP" echo "" echo "GeoIP is a feature that allows converting an IP address to a location (country)" - echo "You have three options in UnrealIRCd:" - echo " classic: This is the DEFAULT geoip engine that should work on all systems" - echo "libmaxminddb: This uses the libmaxminddb library. If you want to use it then" + echo "Possible build options:" + echo " classic: This is the DEFAULT geoip engine. It should work on all systems" + echo " and receives automatic updates." + echo "libmaxminddb: This uses the libmaxminddb library. If you want to use this, then" echo " you need to install the libmaxminddb library on your system first" - echo " none: Don't built with any geoip feature" + echo " none: Don't build with any geoip library (geoip-csv is still built)" echo "Choose one of: classic, libmaxminddb, none" echo $n "[$TEST] -> $c" read cc @@ -814,6 +834,8 @@ if [ -z "$EXTRAPARA" ]; then EXTRAPARA="$TEST" fi +FIX_PATHNAMES + rm -f config.settings cat > config.settings << __EOF__ # diff --git a/Makefile.in b/Makefile.in index 7ce1dd2..ad594f1 100644 --- a/Makefile.in +++ b/Makefile.in @@ -179,13 +179,13 @@ depend: install: all $(INSTALL) -m 0700 -d $(DESTDIR)@BINDIR@ $(INSTALL) -m 0700 src/ircd $(DESTDIR)@BINDIR@/unrealircd + $(INSTALL) -m 0700 src/unrealircdctl $(DESTDIR)@BINDIR@/unrealircdctl $(INSTALL) -m 0700 extras/unrealircd-upgrade-script $(DESTDIR)@BINDIR@/unrealircd-upgrade-script $(INSTALL) -m 0700 -d $(DESTDIR)@DOCDIR@ $(INSTALL) -m 0600 doc/Authors doc/coding-guidelines doc/tao.of.irc doc/KEYS doc/RELEASE-NOTES.md $(DESTDIR)@DOCDIR@ $(INSTALL) -m 0700 -d $(DESTDIR)@CONFDIR@ $(INSTALL) -m 0600 doc/conf/*.conf $(DESTDIR)@CONFDIR@ $(INSTALL) -m 0600 doc/conf/*.motd $(DESTDIR)@CONFDIR@ - $(INSTALL) -m 0600 doc/conf/modules.sources.list $(DESTDIR)@CONFDIR@ ; \ $(INSTALL) -m 0700 unrealircd $(DESTDIR)@SCRIPTDIR@ $(INSTALL) -m 0700 -d $(DESTDIR)@MODULESDIR@ @rm -f $(DESTDIR)@MODULESDIR@/*.so 1>/dev/null 2>&1 diff --git a/Makefile.windows b/Makefile.windows index 39cb1c9..69ec930 100644 --- a/Makefile.windows +++ b/Makefile.windows @@ -165,8 +165,7 @@ CFLAGS=$(DBGCFLAG) $(STDOPTIONS) /FS /MP1 /c /Fosrc/ CFLAGSST=$(DBGCFLAGST) $(STDOPTIONS) /FS /MP1 /c /Fosrc/ LFLAGS=kernel32.lib user32.lib gdi32.lib shell32.lib ws2_32.lib advapi32.lib \ dbghelp.lib oldnames.lib comctl32.lib comdlg32.lib $(STDLIBS) \ - /def:UnrealIRCd.def /implib:UnrealIRCd.lib \ - /nologo $(DBGLFLAG) /out:UnrealIRCd.exe + /nologo $(DBGLFLAG) MODCFLAGS=$(MODDBGCFLAG) $(STDOPTIONS) /D DYNAMIC_LINKING /D MODULE_COMPILE MODLFLAGS=/link /def:src/modules/module.def UnrealIRCd.lib ws2_32.lib $(STDLIBS) @@ -174,10 +173,10 @@ INCLUDES=./include/struct.h ./include/config.h ./include/sys.h \ ./include/common.h ./include/version.h ./include/h.h ./include/numeric.h \ ./include/msg.h ./include/setup.h ./include/dynconf.h -EXP_OBJ_FILES=src/channel.obj src/send.obj src/socket.obj \ - src/conf.obj src/conf_preprocessor.obj \ +EXP_OBJ_FILES=src/ircd_vars.obj src/channel.obj src/send.obj src/socket.obj \ + src/conf.obj src/proc_io_server.obj src/conf_preprocessor.obj \ src/fdlist.obj src/dbuf.obj \ - src/hash.obj src/parse.obj src/ircd.obj \ + src/hash.obj src/parse.obj \ src/whowas.obj \ src/misc.obj src/match.obj src/crule.obj \ src/debug.obj src/support.obj src/list.obj \ @@ -194,7 +193,7 @@ EXP_OBJ_FILES=src/channel.obj src/send.obj src/socket.obj \ src/utf8.obj src/log.obj $(CURLOBJ) OBJ_FILES=$(EXP_OBJ_FILES) src/gui.obj src/service.obj src/windebug.obj src/rtf.obj \ - src/editor.obj src/win.obj + src/editor.obj src/win.obj src/ircd.obj src/proc_io_client.obj DLL_FILES=\ src/modules/account-notify.dll \ @@ -411,7 +410,7 @@ DLL_FILES=\ src/modules/whox.dll -ALL: CONF UNREALSVC.EXE UnrealIRCd.exe MODULES +ALL: CONF unrealircdctl.exe UNREALSVC.EXE UnrealIRCd.exe MODULES CLEAN: -@del /Q /S *.dll *.exe *.obj *.pdb *.res *.lib *.exp *.ilk src\version.c >NUL @@ -424,21 +423,27 @@ CONF: $(CC) src/windows/config.c -@config.exe -UnrealIRCd.exe: $(OBJ_FILES) src/windows/win.res - $(LINK) $(LFLAGS) $(OBJ_FILES) src/windows/win.res /MAP +UnrealIRCd.exe: $(OBJ_FILES) src/ircd.obj src/windows/win.res + $(LINK) $(LFLAGS) /out:UnrealIRCd.exe /def:UnrealIRCd.def /implib:UnrealIRCd.lib $(OBJ_FILES) src/windows/win.res /MAP -@erase src\windows\win.res $(MT) -manifest src\windows\UnrealIRCd.exe.manifest -outputresource:UnrealIRCd.exe;1 -!IFNDEF DEBUGEXTRA - @echo Standard version built -!ELSE - @echo Extra-Debug version built ... -!ENDIF + +unrealircdctl.exe: $(OBJ_FILES) src/unrealircdctl.obj src/proc_io_client.obj + $(LINK) $(LFLAGS) /SUBSYSTEM:CONSOLE /out:unrealircdctl.exe $(OBJ_FILES) src/unrealircdctl.obj + $(MT) -manifest src\windows\unrealircdctl.exe.manifest -outputresource:unrealircdctl.exe;1 + +# alternative option -- FIXME: REMOVE / CHOOSE +#unrealircdctl.exe: $(OBJ_FILES) src/unrealircdctl.obj src/proc_io_client.obj src/windows/unrealircdctl.res +# $(LINK) $(LFLAGS) /out:unrealircdctl.exe $(OBJ_FILES) src/unrealircdctl.obj src/windows/unrealircdctl.res #Source files src/version.obj: src/version.c $(CC) $(CFLAGS) src/version.c +src/ircd_vars.obj: src/ircd_vars.c $(INCLUDES) + $(CC) $(CFLAGS) src/ircd_vars.c + src/parse.obj: src/parse.c $(INCLUDES) $(CC) $(CFLAGS) src/parse.c @@ -485,6 +490,12 @@ src/dns.obj: src/dns.c $(INCLUDES) src/conf.obj: src/conf.c $(INCLUDES) $(CC) $(CFLAGS) src/conf.c +src/proc_io_server.obj: src/proc_io_server.c $(INCLUDES) + $(CC) $(CFLAGS) src/proc_io_server.c + +src/proc_io_client.obj: src/proc_io_client.c $(INCLUDES) + $(CC) $(CFLAGS) src/proc_io_client.c + src/conf_preprocessor.obj: src/conf_preprocessor.c $(INCLUDES) $(CC) $(CFLAGS) src/conf_preprocessor.c @@ -540,6 +551,9 @@ src/win.obj: src/windows/win.c $(INCLUDES) src/unrealsvc.obj: src/windows/unrealsvc.c $(INCLUDES) $(CC) $(CFLAGSST) src/windows/unrealsvc.c +src/unrealircdctl.obj: src/unrealircdctl.c $(INCLUDES) + $(CC) $(CFLAGS) src/unrealircdctl.c + src/modules.obj: src/modules.c $(INCLUDES) $(CC) $(CFLAGS) src/modules.c @@ -623,6 +637,10 @@ src/windows/unrealsvc.res: src/windows/unrealsvc.rc $(RC) /l 0x409 /fosrc/windows/unrealsvc.res /i ./include /i ./src \ /d NDEBUG src/windows/unrealsvc.rc +src/windows/unrealircdctl.res: src/windows/unrealircdctl.rc + $(RC) /l 0x409 /fosrc/windows/unrealircdctl.res /i ./include /i ./src \ + /d NDEBUG src/windows/unrealircdctl.rc + ################# Modules ################# CUSTOMMODULE: src/modules/third/$(MODULEFILE).c diff --git a/configure b/configure index 3b7068c..7dfb3d1 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for unrealircd 6.0.1.1. +# Generated by GNU Autoconf 2.69 for unrealircd 6.0.3. # # Report bugs to . # @@ -580,8 +580,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='unrealircd' PACKAGE_TARNAME='unrealircd' -PACKAGE_VERSION='6.0.1.1' -PACKAGE_STRING='unrealircd 6.0.1.1' +PACKAGE_VERSION='6.0.3' +PACKAGE_STRING='unrealircd 6.0.3' PACKAGE_BUGREPORT='https://bugs.unrealircd.org/' PACKAGE_URL='https://unrealircd.org/' @@ -659,6 +659,7 @@ PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG LDFLAGS_PRIVATELIBS +CONTROLFILE PIDFILE DOCDIR PERMDATADIR @@ -752,6 +753,7 @@ with_tmpdir with_datadir with_docdir with_pidfile +with_controlfile with_privatelibdir with_maxconnections with_no_operoverride @@ -1345,7 +1347,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures unrealircd 6.0.1.1 to adapt to many kinds of systems. +\`configure' configures unrealircd 6.0.3 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1411,7 +1413,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of unrealircd 6.0.1.1:";; + short | recursive ) echo "Configuration of unrealircd 6.0.3:";; esac cat <<\_ACEOF @@ -1460,6 +1462,7 @@ Optional Packages: --with-datadir=path Specify the directory where permanent data is stored --with-docdir=path Specify the directory where documentation is stored --with-pidfile=path Specify the path of the pid file + --with-controlfile=path Specify the path of the control socket --with-privatelibdir=path Specify the directory where private libraries are stored. Disable when building a package for a distro @@ -1586,7 +1589,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -unrealircd configure 6.0.1.1 +unrealircd configure 6.0.3 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1955,7 +1958,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by unrealircd $as_me 6.0.1.1, which was +It was created by unrealircd $as_me 6.0.3, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2363,7 +2366,7 @@ _ACEOF # Minor version number (e.g.: Z in X.Y.Z) -UNREAL_VERSION_MINOR="1" +UNREAL_VERSION_MINOR="3" cat >>confdefs.h <<_ACEOF #define UNREAL_VERSION_MINOR $UNREAL_VERSION_MINOR @@ -2373,7 +2376,7 @@ _ACEOF # 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 # string instead of an integer because it contains arbitrary data. -UNREAL_VERSION_SUFFIX=".1" +UNREAL_VERSION_SUFFIX="" cat >>confdefs.h <<_ACEOF #define UNREAL_VERSION_SUFFIX "$UNREAL_VERSION_SUFFIX" @@ -6548,6 +6551,25 @@ fi +# Check whether --with-controlfile was given. +if test "${with_controlfile+set}" = set; then : + withval=$with_controlfile; +cat >>confdefs.h <<_ACEOF +#define CONTROLFILE "$withval" +_ACEOF + + CONTROLFILE="$withval" +else + +cat >>confdefs.h <<_ACEOF +#define CONTROLFILE "$HOME/unrealircd/data/unrealircd.ctl" +_ACEOF + + CONTROLFILE="$HOME/unrealircd/data/unrealircd.ctl" +fi + + + # Check whether --with-privatelibdir was given. if test "${with_privatelibdir+set}" = set; then : withval=$with_privatelibdir; @@ -6589,6 +6611,7 @@ fi + # Check whether --with-maxconnections was given. if test "${with_maxconnections+set}" = set; then : withval=$with_maxconnections; ac_fd=$withval @@ -7438,7 +7461,7 @@ fi if test "$has_system_pcre2" = "no"; then : -pcre2_version="10.36" +pcre2_version="10.39" { $as_echo "$as_me:${as_lineno-$LINENO}: result: extracting PCRE2 regex library" >&5 $as_echo "extracting PCRE2 regex library" >&6; } cur_dir=`pwd` @@ -7455,7 +7478,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: configuring PCRE2 regex library" >&5 $as_echo "configuring PCRE2 regex library" >&6; } cd pcre2-$pcre2_version -./configure --enable-jit --enable-shared --disable-unicode --prefix=$cur_dir/extras/pcre2 --libdir=$PRIVATELIBDIR || exit 1 +./configure --enable-jit --enable-shared --prefix=$cur_dir/extras/pcre2 --libdir=$PRIVATELIBDIR || exit 1 { $as_echo "$as_me:${as_lineno-$LINENO}: result: compiling PCRE2 regex library" >&5 $as_echo "compiling PCRE2 regex library" >&6; } $ac_cv_prog_MAKER || exit 1 @@ -7798,7 +7821,7 @@ fi if test "$has_system_cares" = "no"; then : -cares_version="1.17.2" +cares_version="1.18.1" { $as_echo "$as_me:${as_lineno-$LINENO}: result: extracting c-ares resolver library" >&5 $as_echo "extracting c-ares resolver library" >&6; } cur_dir=`pwd` @@ -9391,7 +9414,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by unrealircd $as_me 6.0.1.1, which was +This file was extended by unrealircd $as_me 6.0.3, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -9454,7 +9477,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -unrealircd config.status 6.0.1.1 +unrealircd config.status 6.0.3 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 9dff663..e3a5390 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ dnl src/windows/unrealinst.iss dnl doc/Config.header dnl src/version.c.SH -AC_INIT([unrealircd], [6.0.1.1], [https://bugs.unrealircd.org/], [], [https://unrealircd.org/]) +AC_INIT([unrealircd], [6.0.3], [https://bugs.unrealircd.org/], [], [https://unrealircd.org/]) AC_CONFIG_SRCDIR([src/ircd.c]) AC_CONFIG_HEADER([include/setup.h]) AC_CONFIG_AUX_DIR([autoconf]) @@ -34,13 +34,13 @@ UNREAL_VERSION_MAJOR=["0"] 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) -UNREAL_VERSION_MINOR=["1"] +UNREAL_VERSION_MINOR=["3"] 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 # marker. (e.g.: -rcX for unrealircd-3.2.9-rcX). This macro is a # string instead of an integer because it contains arbitrary data. -UNREAL_VERSION_SUFFIX=[".1"] +UNREAL_VERSION_SUFFIX=[""] 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) @@ -484,6 +484,12 @@ AC_ARG_WITH(pidfile, [AS_HELP_STRING([--with-pidfile=path],[Specify the path of [AC_DEFINE_UNQUOTED([PIDFILE], ["$HOME/unrealircd/data/unrealircd.pid"], [Define the path of the pid file]) PIDFILE="$HOME/unrealircd/data/unrealircd.pid"]) +AC_ARG_WITH(controlfile, [AS_HELP_STRING([--with-controlfile=path],[Specify the path of the control socket])], + [AC_DEFINE_UNQUOTED([CONTROLFILE], ["$withval"], [Define the path of the control socket]) + CONTROLFILE="$withval"], + [AC_DEFINE_UNQUOTED([CONTROLFILE], ["$HOME/unrealircd/data/unrealircd.ctl"], [Define the path of the control socket]) + CONTROLFILE="$HOME/unrealircd/data/unrealircd.ctl"]) + dnl Ensure that this “feature” can be disabled as it makes it harder to package unrealircd. dnl Users have always been able to specify “./configure LDFLAGS=-Wl,-rpath,/path/to/blah”—binki AC_ARG_WITH(privatelibdir, [AS_HELP_STRING([--with-privatelibdir=path],[Specify the directory where private libraries are stored. Disable when building a package for a distro])], @@ -514,6 +520,7 @@ dnl well, Because DATADIR conflicts with the Windows SDK header files.. amazing. AC_SUBST(PERMDATADIR) AC_SUBST(DOCDIR) AC_SUBST(PIDFILE) +AC_SUBST(CONTROLFILE) AC_SUBST(LDFLAGS_PRIVATELIBS) AC_ARG_WITH(maxconnections, [AS_HELP_STRING([--with-maxconnections=size], [Specify the max file descriptors to use])], @@ -576,7 +583,7 @@ AS_IF([test "x$PRIVATELIBDIR" != "x"], [rm -f "$PRIVATELIBDIR/"libpcre2*])],[has AS_IF([test "$has_system_pcre2" = "no"], [ dnl REMEMBER TO CHANGE WITH A NEW PCRE2 RELEASE! -pcre2_version="10.36" +pcre2_version="10.39" AC_MSG_RESULT(extracting PCRE2 regex library) cur_dir=`pwd` cd extras @@ -593,7 +600,7 @@ else fi AC_MSG_RESULT(configuring PCRE2 regex library) cd pcre2-$pcre2_version -./configure --enable-jit --enable-shared --disable-unicode --prefix=$cur_dir/extras/pcre2 --libdir=$PRIVATELIBDIR || exit 1 +./configure --enable-jit --enable-shared --prefix=$cur_dir/extras/pcre2 --libdir=$PRIVATELIBDIR || exit 1 AC_MSG_RESULT(compiling PCRE2 regex library) $ac_cv_prog_MAKER || exit 1 AC_MSG_RESULT(installing PCRE2 regex library) @@ -716,7 +723,7 @@ AS_IF([test "$has_system_cares" = "no"], [ dnl REMEMBER TO CHANGE WITH A NEW C-ARES RELEASE! dnl NOTE: when changing this here, ALSO change it in extras/curlinstall dnl and in the comment in this file around line 400! -cares_version="1.17.2" +cares_version="1.18.1" AC_MSG_RESULT(extracting c-ares resolver library) cur_dir=`pwd` cd extras diff --git a/doc/Config.header b/doc/Config.header index 98ec32f..96c64d7 100644 --- a/doc/Config.header +++ b/doc/Config.header @@ -7,7 +7,7 @@ \___/|_| |_|_| \___|\__,_|_|\___/\_| \_| \____/\__,_| Configuration Program - for UnrealIRCd 6.0.1.1 + for UnrealIRCd 6.0.3 This program will help you to compile your IRC server, and ask you questions regarding the compile-time settings of it during the process. diff --git a/doc/RELEASE-NOTES.md b/doc/RELEASE-NOTES.md index e0ac30d..197a2ab 100644 --- a/doc/RELEASE-NOTES.md +++ b/doc/RELEASE-NOTES.md @@ -1,8 +1,139 @@ +UnrealIRCd 6.0.3 +================= + +A number of serious issues were discovered in UnrealIRCd 6. Among these is +an issue which will likely crash the IRCd sooner or later if you /REHASH +with any active clients connected. +We suggest everyone who is running UnrealIRCd 6 to upgrade to 6.0.3. + +If you are already running UnrealIRCd 6 then read below. Otherwise, jump +straight to the [summary about UnrealIRCd 6](#Summary) to learn more +about UnrealIRCd 6. + +Fixes: +* Crash in `WATCH` if the IRCd has been rehashed at least once. After doing + a `REHASH` with active clients it will likely corrupt memory. It may take + several days until after the rehash for the crash to occur, or even + weeks/months on smaller networks (accidental triggering, that is). +* A `REHASH` with certain remote includes setups could cause a crash or + other weird and confusing problems such as complaining about unable + to open an ipv6-database or missing snomask configuration. + This only affected some people with remote includes, not all. +* Potential out-of-bounds write in sending code. In practice it seems + harmless on most servers but this cannot be 100% guaranteed. +* Unlikely triggered log message would log uninitialized stack data to the + log file or send it to ircops. +* Channel ops could not remove halfops from a user (`-h`). +* After using the `RESTART` command (not recommended) the new IRCd was + often no longer writing to log files. +* Fix compile problem if you choose to use cURL remote includes but don't + have cURL on the system and ask UnrealIRCd to compile cURL. + +Enhancements: +* The default text log format on disk changed. It now includes the server + name where the event was generated. Without this, it was sometimes + difficult to trace problems, since previously it sometimes looked like + there was a problem on your server when it was actually another server + on the network. + * Old log format: `[DATE TIME] subsystem.EVENT_ID loglevel: ........` + * New log format: `[DATE TIME] servername subsystem.EVENT_ID loglevel: ........` + +Changes: +* Any MOTD lines added by services via + [`SVSMOTD`](https://www.unrealircd.org/docs/MOTD_and_Rules#SVSMOTD) + are now shown at the end of the MOTD-on-connect (unless using a shortmotd). + Previously the lines were only shown if you manually ran the `MOTD` command. + +Developers and protocol: +* `LIST C/dev/null 2>&1 exit $EX diff --git a/extras/build-tests/windows/build.bat b/extras/build-tests/windows/build.bat index 6f8ed3c..f706cb1 100644 --- a/extras/build-tests/windows/build.bat +++ b/extras/build-tests/windows/build.bat @@ -36,11 +36,11 @@ rem Now the actual build rem - First this, otherwise JOM will fail IF NOT EXIST src\version.c nmake -f Makefile.windows CONF rem - Then build most of UnrealIRCd.exe etc -call extras\build-tests\windows\compilecmd\%SHORTNAME%.bat UNREALSVC.EXE UnrealIRCd.exe +call extras\build-tests\windows\compilecmd\%SHORTNAME%.bat UNREALSVC.EXE UnrealIRCd.exe unrealircdctl.exe rem - It will fail due to missing symbolfile, which we create here.. nmake -f makefile.windows SYMBOLFILE rem - Then we finalize building UnrealIRCd.exe: should be no error -call extras\build-tests\windows\compilecmd\%SHORTNAME%.bat UNREALSVC.EXE UnrealIRCd.exe +call extras\build-tests\windows\compilecmd\%SHORTNAME%.bat UNREALSVC.EXE UnrealIRCd.exe unrealircdctl.exe if %ERRORLEVEL% NEQ 0 EXIT /B 1 rem - Build all the modules (DLL files): should be no error call extras\build-tests\windows\compilecmd\%SHORTNAME%.bat MODULES diff --git a/extras/c-ares.tar.gz b/extras/c-ares.tar.gz index bb16c09..7523b17 100644 Binary files a/extras/c-ares.tar.gz and b/extras/c-ares.tar.gz differ diff --git a/extras/curlinstall b/extras/curlinstall index 85bce4e..9e0debd 100755 --- a/extras/curlinstall +++ b/extras/curlinstall @@ -2,10 +2,8 @@ URL="https://www.unrealircd.org/files/curl-latest.tar.gz" OUTF="curl-latest.tar.gz" OUTD="curl-latest" -ARESPATH="`pwd`/extras/c-ares" UNREALDIR="`pwd`" -CARESVERSION="1.17.2" -LIBDIR="$1" +PRIVATELIBDIR="$1" if [ "x$1" = "x" ]; then echo "You should (no longer) run this program directly." @@ -73,26 +71,9 @@ else n="-n" fi -if [ ! -d "$ARESPATH/lib" ]; then - echo "c-ares has not been build yet, let's do that now..." - cd ../extras/ - tar xzf c-ares.tar.gz || exit 1 - cd c-ares-$CARESVERSION || exit 1 - ./configure --prefix=$ARESPATH || exit 1 - (make && make install) || exit 1 - cd ../../tmp/ - echo "c-ares built." - echo "" -fi - # We assume curl has been packaged in a way it will extract to "$OUTD"/ cd "$OUTD" || exit 1 echo "Building and installing libcurl" -CPPFLAGS="-I$ARESPATH/include" ./configure --prefix=$UNREALDIR/extras/curl --libdir=$LIBDIR --enable-shared \ - --enable-ares=$ARESPATH --with-openssl -cp -R $ARESPATH/lib ares +./configure --prefix=$UNREALDIR/extras/curl --libdir=$PRIVATELIBDIR --enable-shared --with-openssl make && make install - -#cp $ARESPATH/lib/libcares.a $HOME/curl/lib -# that isn't needed anymore as the lib is already in unreal... diff --git a/extras/doxygen/Doxyfile b/extras/doxygen/Doxyfile index f9e71a9..469e8ca 100644 --- a/extras/doxygen/Doxyfile +++ b/extras/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = "UnrealIRCd" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 6.0.1.1 +PROJECT_NUMBER = 6.0.3 # 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 diff --git a/extras/pcre2.tar.gz b/extras/pcre2.tar.gz index d0c09da..d16fe48 100644 Binary files a/extras/pcre2.tar.gz and b/extras/pcre2.tar.gz differ diff --git a/include/dynconf.h b/include/dynconf.h index 978f444..54b1c16 100644 --- a/include/dynconf.h +++ b/include/dynconf.h @@ -170,6 +170,7 @@ struct Configuration { char *stats_server; char *sasl_server; int server_notice_colors; + int server_notice_show_event; }; extern MODVAR Configuration iConf; diff --git a/include/h.h b/include/h.h index 6b380f5..2d11547 100644 --- a/include/h.h +++ b/include/h.h @@ -28,6 +28,34 @@ #include "setup.h" #include "fdlist.h" +extern int dorehash, dorestart, doreloadcert; +#ifndef _WIN32 +extern char **myargv; +#else +extern LPCSTR cmdLine; +#endif +/* Externals */ +extern MODVAR char *buildid; +extern MODVAR char backupbuf[8192]; +extern EVENT(unrealdns_removeoldrecords); +extern EVENT(unrealdb_expire_secret_cache); +extern void init_glines(void); +extern void tkl_init(void); +extern void process_clients(void); +extern void unrealdb_test(void); +extern void ignore_this_signal(); +extern void s_rehash(); +extern void s_reloadcert(); +extern void s_restart(); +extern void s_die(); +#ifndef _WIN32 +// nix specific +extern char unreallogo[]; +#else +// windows specific +extern SERVICE_STATUS_HANDLE IRCDStatusHandle; +extern SERVICE_STATUS IRCDStatus; +#endif extern MODVAR char *extraflags; extern MODVAR int tainted; extern MODVAR Member *freemember; @@ -117,7 +145,7 @@ extern void ipport_seperate(const char *string, char **ip, char **port); extern ConfigItem_class *find_class(const char *name); extern ConfigItem_oper *find_oper(const char *name); extern ConfigItem_operclass *find_operclass(const char *name); -extern ConfigItem_listen *find_listen(const char *ipmask, int port, int ipv6); +extern ConfigItem_listen *find_listen(const char *ipmask, int port, SocketType socket_type); extern ConfigItem_sni *find_sni(const char *name); extern ConfigItem_ulines *find_uline(const char *host); extern ConfigItem_tld *find_tld(Client *cptr); @@ -153,6 +181,7 @@ extern MODVAR struct list_head lclient_list; extern MODVAR struct list_head server_list; extern MODVAR struct list_head oper_list; extern MODVAR struct list_head unknown_list; +extern MODVAR struct list_head control_list; extern MODVAR struct list_head global_server_list; extern MODVAR struct list_head dead_list; extern RealCommand *find_command(const char *cmd, int flags); @@ -489,6 +518,7 @@ extern int channel_canjoin(Client *client, const char *name); extern char *collapse(char *pattern); extern void dcc_sync(Client *client); extern void request_rehash(Client *client); +extern int rehash_internal(Client *client); extern void s_die(); extern int match_simple(const char *mask, const char *name); extern int match_esc(const char *mask, const char *name); @@ -507,7 +537,7 @@ extern void rehash_motdrules(); extern void read_motd(const char *filename, MOTDFile *motd); /* s_serv.c */ extern void send_proto(Client *, ConfigItem_link *); extern void unload_all_modules(void); -extern void set_sock_opts(int fd, Client *cptr, int ipv6); +extern void set_sock_opts(int fd, Client *cptr, SocketType socket_type); extern void stripcrlf(char *line); extern int strnatcmp(char const *a, char const *b); extern int strnatcasecmp(char const *a, char const *b); @@ -663,7 +693,6 @@ extern int spamfilter_getconftargets(const char *s); extern void remove_all_snomasks(Client *client); extern void remove_oper_modes(Client *client); extern char *spamfilter_inttostring_long(int v); -extern MODVAR char backupbuf[]; extern int is_invited(Client *client, Channel *channel); extern void channel_modes(Client *client, char *mbuf, char *pbuf, size_t mbuf_size, size_t pbuf_size, Channel *channel, int hide_local_modes); extern int op_can_override(const char *acl, Client *client,Channel *channel,void* extra); @@ -859,6 +888,7 @@ extern const char *cmdname_by_spamftarget(int target); extern void unrealdns_delreq_bycptr(Client *cptr); extern void unrealdns_gethostbyname_link(const char *name, ConfigItem_link *conf, int ipv4_only); extern void unrealdns_delasyncconnects(void); +extern EVENT(unrealdns_timeout); extern int is_autojoin_chan(const char *chname); extern void unreal_free_hostent(struct hostent *he); extern struct hostent *unreal_create_hostent(const char *name, const char *ip); @@ -915,10 +945,14 @@ extern void report_crash(void); extern void modulemanager(int argc, char *argv[]); extern int inet_pton4(const char *src, unsigned char *dst); extern int inet_pton6(const char *src, unsigned char *dst); -extern int unreal_bind(int fd, const char *ip, int port, int ipv6); +extern int unreal_bind(int fd, const char *ip, int port, SocketType socket_type); extern int unreal_connect(int fd, const char *ip, int port, int ipv6); extern int is_valid_ip(const char *str); extern int ipv6_capable(void); +extern int unix_sockets_capable(void); +#ifdef _WIN32 +extern void init_winsock(void); +#endif extern MODVAR Client *remote_rehash_client; extern MODVAR int debugfd; extern void convert_to_absolute_path(char **path, const char *reldir); @@ -929,7 +963,7 @@ extern Cmode_t get_extmode_bitbychar(char m); extern long find_user_mode(char mode); extern void start_listeners(void); extern void buildvarstring(const char *inbuf, char *outbuf, size_t len, const char *name[], const char *value[]); -extern void reinit_tls(void); +extern int reinit_tls(void); extern CMD_FUNC(cmd_error); extern CMD_FUNC(cmd_dns); extern CMD_FUNC(cmd_info); @@ -1135,6 +1169,8 @@ extern void flood_limit_exceeded_log(Client *client, const char *floodname); /* logging */ extern int config_test_log(ConfigFile *conf, ConfigEntry *ce); extern int config_run_log(ConfigFile *conf, ConfigEntry *ce); +extern const char *log_level_terminal_color(LogLevel loglevel); +#define TERMINAL_COLOR_RESET "\033[0m" extern LogType log_type_stringtoval(const char *str); extern const char *log_type_valtostring(LogType v); #ifdef DEBUGMODE @@ -1208,3 +1244,8 @@ extern void make_umodestr(void); extern void initwhowas(void); extern void uid_init(void); extern const char *uid_get(void); +/* proc i/o */ +extern void add_proc_io_server(void); +extern void procio_post_rehash(int failure); +/* end of proc i/o */ +extern int minimum_msec_since_last_run(struct timeval *tv_old, long minimum); diff --git a/include/modules.h b/include/modules.h index 62054cf..c535144 100644 --- a/include/modules.h +++ b/include/modules.h @@ -394,7 +394,7 @@ typedef enum ExtbanType { #define EXTBANTABLESZ 32 typedef enum ExtbanOptions { - EXTBOPT_CHSVSMODE=0x1, /**< SVSMODE -b/-e/-I will clear this ban */ + EXTBOPT_CHSVSMODE=0x1, /**< SVSMODE -b/-e/-I will clear this ban (UNUSED as of 6.0.1+) */ EXTBOPT_ACTMODIFIER=0x2, /**< Action modifier (not a matcher). These are extended bans like ~q/~n/~j. */ EXTBOPT_NOSTACKCHILD=0x4, /**< Disallow prefixing with another extban. Eg disallow ~n:~T:censor:xyz */ EXTBOPT_INVEX=0x8, /**< Available for use with +I too */ @@ -1151,12 +1151,14 @@ extern void SavePersistentLongX(ModuleInfo *modinfo, const char *varshortname, l #define HOOKTYPE_POST_LOCAL_NICKCHANGE 106 /** See hooktype_post_remote_nickchange() */ #define HOOKTYPE_POST_REMOTE_NICKCHANGE 107 -/** See hooktype_userhost_changed() */ -#define HOOKTYPE_USERHOST_CHANGED 108 -/** See hooktype_realname_changed() */ -#define HOOKTYPE_REALNAME_CHANGED 109 +/** See hooktype_userhost_change() */ +#define HOOKTYPE_USERHOST_CHANGE 108 +/** See hooktype_realname_change() */ +#define HOOKTYPE_REALNAME_CHANGE 109 /** See hooktype_can_set_topic() */ #define HOOKTYPE_CAN_SET_TOPIC 110 +/** See hooktype_ip_change() */ +#define HOOKTYPE_IP_CHANGE 111 /* Adding a new hook here? * 1) Add the #define HOOKTYPE_.... with a new number * 2) Add a hook prototype (see below) @@ -2127,14 +2129,22 @@ int hooktype_post_remote_nickchange(Client *client, MessageTag *mtags, const cha * @param oldhost Old hostname of the client * @return The return value is ignored (use return 0) */ - -int hooktype_realname_changed(Client *client, const char *oldinfo); +int hooktype_userhost_change(Client *client, const char *olduser, const char *oldhost); + /** Called when user realname has changed. * @param client The client whose realname has changed * @param oldinfo Old realname of the client * @return The return value is ignored (use return 0) */ -int hooktype_userhost_changed(Client *client, const char *olduser, const char *oldhost); +int hooktype_realname_change(Client *client, const char *oldinfo); + +/** Called when changing IP (eg due to PROXY/WEBIRC/etc). + * @param client The client whose IP has changed + * @param oldip Old IP of the client + * @return The return value is ignored (use return 0) + */ +int hooktype_ip_change(Client *client, const char *oldip); + /** @} */ #ifdef GCC_TYPECHECKING @@ -2247,8 +2257,9 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum ((hooktype == HOOKTYPE_IS_INVITED) && !ValidateHook(hooktype_is_invited, func)) || \ ((hooktype == HOOKTYPE_POST_LOCAL_NICKCHANGE) && !ValidateHook(hooktype_post_local_nickchange, func)) || \ ((hooktype == HOOKTYPE_POST_REMOTE_NICKCHANGE) && !ValidateHook(hooktype_post_remote_nickchange, func)) || \ - ((hooktype == HOOKTYPE_USERHOST_CHANGED) && !ValidateHook(hooktype_userhost_changed, func)) || \ - ((hooktype == HOOKTYPE_REALNAME_CHANGED) && !ValidateHook(hooktype_realname_changed, func)) )\ + ((hooktype == HOOKTYPE_USERHOST_CHANGE) && !ValidateHook(hooktype_userhost_change, func)) || \ + ((hooktype == HOOKTYPE_REALNAME_CHANGE) && !ValidateHook(hooktype_realname_change, func)) || \ + ((hooktype == HOOKTYPE_IP_CHANGE) && !ValidateHook(hooktype_ip_change, func)) ) \ _hook_error_incompatible(); #endif /* GCC_TYPECHECKING */ diff --git a/include/numeric.h b/include/numeric.h index a380889..5380bbe 100644 --- a/include/numeric.h +++ b/include/numeric.h @@ -37,6 +37,8 @@ #define RPL_REDIR 10 +#define RPL_MAPUSERS 18 + #define RPL_REMOTEISUPPORT 105 /* @@ -349,10 +351,11 @@ #define STR_RPL_CREATED /* 003 */ ":This server was created %s" #define STR_RPL_MYINFO /* 004 */ "%s %s %s %s" #define STR_RPL_ISUPPORT /* 005 */ "%s :are supported by this server" -#define STR_RPL_MAP /* 006 */ ":%s%-*s(%ld) %s" +#define STR_RPL_MAP /* 006 */ ":%s%s %s | Users: %*ld (%*.2f%%)%s" #define STR_RPL_MAPEND /* 007 */ ":End of /MAP" #define STR_RPL_SNOMASK /* 008 */ "+%s :Server notice mask" #define STR_RPL_REDIR /* 010 */ "%s %d :Please use this Server/Port instead" +#define STR_RPL_MAPUSERS /* 018 */ ":%d server%s and %d user%s, average %.2f users per server" #define STR_RPL_REMOTEISUPPORT /* 105 */ "%s :are supported by this server" #define STR_RPL_TRACELINK /* 200 */ "Link %s%s %s %s" #define STR_RPL_TRACECONNECTING /* 201 */ "Attempt %s %s" diff --git a/include/setup.h.in b/include/setup.h.in index 18f6908..16b097c 100644 --- a/include/setup.h.in +++ b/include/setup.h.in @@ -12,6 +12,9 @@ /* Define the location of the configuration files */ #undef CONFDIR +/* Define the path of the control socket */ +#undef CONTROLFILE + /* Define the location of permanent data files */ #undef DATADIR diff --git a/include/struct.h b/include/struct.h index d820f2b..e088b77 100644 --- a/include/struct.h +++ b/include/struct.h @@ -263,12 +263,18 @@ typedef struct Log Log; struct Log { Log *prev, *next; LogSource *sources; + int type; char destination[CHANNELLEN+1]; + int show_event; + /* for destination::file */ char *file; char *filefmt; long maxsize; - int type; int logfd; + /* for destination::channel */ + int color; + int json_message_tag; + int oper_only; }; /** This is used for deciding the in logs[] and temp_logs[] */ @@ -317,6 +323,7 @@ typedef enum LogDestination { LOG_DEST_SNOMASK=0, LOG_DEST_OPER=1, LOG_DEST_REMO * @{ */ typedef enum ClientStatus { + CLIENT_STATUS_CONTROL = -8, /**< Client is on the control channel */ CLIENT_STATUS_LOG = -7, /**< Client is a log file */ CLIENT_STATUS_TLS_STARTTLS_HANDSHAKE = -8, /**< Client is doing a STARTTLS handshake */ CLIENT_STATUS_CONNECTING = -6, /**< Client is an outgoing connect */ @@ -339,6 +346,7 @@ typedef enum ClientStatus { /** Client is not fully registered yet. May become a user or a server, we don't know yet. */ #define IsUnknown(x) (((x)->status == CLIENT_STATUS_UNKNOWN) || ((x)->status == CLIENT_STATUS_TLS_STARTTLS_HANDSHAKE)) #define IsServer(x) ((x)->status == CLIENT_STATUS_SERVER) /**< Is a server that has completed the connection handshake */ +#define IsControl(x) ((x)->status == CLIENT_STATUS_CONTROL) /**< Is on the control channel (not on IRC) */ #define IsLog(x) ((x)->status == CLIENT_STATUS_LOG) /**< Is a log file, not a user or server */ #define IsStartTLSHandshake(x) ((x)->status == CLIENT_STATUS_TLS_STARTTLS_HANDSHAKE) /**< Currently doing a STARTTLS handshake */ #define IsTLSAcceptHandshake(x) ((x)->status == CLIENT_STATUS_TLS_ACCEPT_HANDSHAKE) /**< Currently doing a TLS handshake - incoming */ @@ -355,6 +363,8 @@ typedef enum ClientStatus { #define SetServer(x) ((x)->status = CLIENT_STATUS_SERVER) #define SetUser(x) ((x)->status = CLIENT_STATUS_USER) #define SetLog(x) ((x)->status = CLIENT_STATUS_LOG) +#define SetControl(x) ((x)->status = CLIENT_STATUS_CONTROL) +#define SetUser(x) ((x)->status = CLIENT_STATUS_USER) /** @} */ @@ -366,7 +376,7 @@ typedef enum ClientStatus { #define CLIENT_FLAG_DEAD 0x00000002 /**< Client is dead: already quit/exited and removed from all lists -- Remaining part will soon be freed in main loop */ #define CLIENT_FLAG_DEADSOCKET 0x00000004 /**< Local socket is dead but otherwise the client still exists fully -- Will soon exit in main loop */ #define CLIENT_FLAG_KILLED 0x00000008 /**< Prevents "QUIT" from being sent for this */ -#define CLIENT_FLAG_IPV6 0x00000010 /**< Connection is using IPv6 */ +#define CLIENT_FLAG_MONITOR_REHASH 0x00000010 /**< Client is monitoring rehash output */ #define CLIENT_FLAG_OUTGOING 0x00000020 /**< Outgoing connection (do not touch cptr->listener->clients) */ #define CLIENT_FLAG_CLOSING 0x00000040 /**< Set when closing to suppress errors */ #define CLIENT_FLAG_LISTEN 0x00000080 /**< Used to mark clients which we listen() on */ @@ -464,8 +474,8 @@ typedef enum ClientStatus { #define IsDNSLookup(x) ((x)->flags & CLIENT_FLAG_DNSLOOKUP) #define IsEAuth(x) ((x)->flags & CLIENT_FLAG_EAUTH) #define IsIdentSuccess(x) ((x)->flags & CLIENT_FLAG_IDENTSUCCESS) -#define IsIPV6(x) ((x)->flags & CLIENT_FLAG_IPV6) #define IsKilled(x) ((x)->flags & CLIENT_FLAG_KILLED) +#define IsMonitorRehash(x) ((x)->flags & CLIENT_FLAG_MONITOR_REHASH) #define IsListening(x) ((x)->flags & CLIENT_FLAG_LISTEN) #define IsLocalhost(x) ((x)->flags & CLIENT_FLAG_LOCALHOST) #define IsMap(x) ((x)->flags & CLIENT_FLAG_MAP) @@ -496,8 +506,8 @@ typedef enum ClientStatus { #define SetDNSLookup(x) do { (x)->flags |= CLIENT_FLAG_DNSLOOKUP; } while(0) #define SetEAuth(x) do { (x)->flags |= CLIENT_FLAG_EAUTH; } while(0) #define SetIdentSuccess(x) do { (x)->flags |= CLIENT_FLAG_IDENTSUCCESS; } while(0) -#define SetIPV6(x) do { (x)->flags |= CLIENT_FLAG_IPV6; } while(0) #define SetKilled(x) do { (x)->flags |= CLIENT_FLAG_KILLED; } while(0) +#define SetMonitorRehash(x) do { (x)->flags |= CLIENT_FLAG_MONITOR_REHASH; } while(0) #define SetListening(x) do { (x)->flags |= CLIENT_FLAG_LISTEN; } while(0) #define SetLocalhost(x) do { (x)->flags |= CLIENT_FLAG_LOCALHOST; } while(0) #define SetMap(x) do { (x)->flags |= CLIENT_FLAG_MAP; } while(0) @@ -526,8 +536,8 @@ typedef enum ClientStatus { #define ClearDNSLookup(x) do { (x)->flags &= ~CLIENT_FLAG_DNSLOOKUP; } while(0) #define ClearEAuth(x) do { (x)->flags &= ~CLIENT_FLAG_EAUTH; } while(0) #define ClearIdentSuccess(x) do { (x)->flags &= ~CLIENT_FLAG_IDENTSUCCESS; } while(0) -#define ClearIPV6(x) do { (x)->flags &= ~CLIENT_FLAG_IPV6; } while(0) #define ClearKilled(x) do { (x)->flags &= ~CLIENT_FLAG_KILLED; } while(0) +#define ClearMonitorRehash(x) do { (x)->flags &= ~CLIENT_FLAG_MONITOR_REHASH; } while(0) #define ClearListening(x) do { (x)->flags &= ~CLIENT_FLAG_LISTEN; } while(0) #define ClearLocalhost(x) do { (x)->flags &= ~CLIENT_FLAG_LOCALHOST; } while(0) #define ClearMap(x) do { (x)->flags &= ~CLIENT_FLAG_MAP; } while(0) @@ -546,6 +556,9 @@ typedef enum ClientStatus { #define ClearULine(x) do { (x)->flags &= ~CLIENT_FLAG_ULINE; } while(0) #define ClearVirus(x) do { (x)->flags &= ~CLIENT_FLAG_VIRUS; } while(0) #define ClearIdentLookupSent(x) do { (x)->flags &= ~CLIENT_FLAG_IDENTLOOKUPSENT; } while(0) +#define IsIPV6(x) ((x)->local->socket_type == SOCKET_TYPE_IPV6) +#define IsUnixSocket(x) ((x)->local->socket_type == SOCKET_TYPE_UNIX) +#define SetIPV6(x) do { (x)->local->socket_type = SOCKET_TYPE_IPV6; } while(0) /** @} */ @@ -779,11 +792,11 @@ struct LoopStruct { unsigned do_bancheck : 1; /* perform *line bancheck? */ unsigned do_bancheck_spamf_user : 1; /* perform 'user' spamfilter bancheck */ unsigned do_bancheck_spamf_away : 1; /* perform 'away' spamfilter bancheck */ - unsigned rehashing : 1; unsigned terminating : 1; unsigned config_load_failed : 1; unsigned rehash_download_busy : 1; /* don't return "all downloads complete", needed for race condition */ unsigned tainted : 1; + int rehashing; Client *rehash_save_client; void (*boot_function)(); }; @@ -851,6 +864,8 @@ struct SWhois { #define CMD_VIRUS 0x0080 /** Command requires IRCOp privileges */ #define CMD_OPER 0x0200 +/** Command is for control channel only (unrealircd.ctl socket) */ +#define CMD_CONTROL 0x0400 /** Command function - used by all command handlers. * This is used in the code like
CMD_FUNC(cmd_yourcmd)
as a function definition. @@ -1212,6 +1227,7 @@ extern void unload_all_unused_moddata(void); #define LISTENER_TLS 0x000010 #define LISTENER_BOUND 0x000020 #define LISTENER_DEFER_ACCEPT 0x000040 +#define LISTENER_CONTROL 0x000080 /**< Control channel */ #define IsServersOnlyListener(x) ((x) && ((x)->options & LISTENER_SERVERSONLY)) @@ -1240,6 +1256,7 @@ typedef enum FloodOption { FLD_KNOCK = 4, /**< knock-flood */ FLD_CONVERSATIONS = 5, /**< max-concurrent-conversations */ FLD_LAG_PENALTY = 6, /**< lag-penalty / lag-penalty-bytes */ + FLD_VHOST = 7, /**< vhost-flood */ } FloodOption; #define MAXFLOODOPTIONS 10 @@ -1251,6 +1268,11 @@ struct TrafficStats { long long bytes_received; /* Received bytes */ }; +/** Socket type (IPv4, IPv6, UNIX) */ +typedef enum { + SOCKET_TYPE_IPV4=0, SOCKET_TYPE_IPV6=1, SOCKET_TYPE_UNIX=2 +} SocketType; + /** 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 * @{ @@ -1288,6 +1310,7 @@ struct Client { */ struct LocalClient { int fd; /**< File descriptor, can be <0 if socket has been closed already. */ + SocketType socket_type; /**< Type of socket: IPv4, IPV6, UNIX */ SSL *ssl; /**< OpenSSL/LibreSSL struct for TLS connection */ time_t fake_lag; /**< Time when user will next be allowed to send something (actually fake_lag #include +#include #include #else #include #include +#include #endif #ifndef _WIN32 diff --git a/include/version.h b/include/version.h index 00f5e62..c1cfb40 100644 --- a/include/version.h +++ b/include/version.h @@ -54,7 +54,7 @@ * 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. */ -#define UNREAL_VERSION_TIME 202148 +#define UNREAL_VERSION_TIME 202204 #define UNREAL_VERSION ((UNREAL_VERSION_GENERATION << 24) + (UNREAL_VERSION_MAJOR << 16) + (UNREAL_VERSION_MINOR << 8)) #define UnrealProtocol 6000 diff --git a/include/windows/setup.h b/include/windows/setup.h index 12d49c4..e4242cc 100644 --- a/include/windows/setup.h +++ b/include/windows/setup.h @@ -33,6 +33,7 @@ #define CACHEDIR "cache" #define TMPDIR "tmp" #define PIDFILE PERMDATADIR"/unrealircd.pid" +#define CONTROLFILE PERMDATADIR"/unrealircd.ctl" #define NO_U_TYPES #define NEED_U_INT32_T #define strcasecmp _stricmp @@ -61,10 +62,10 @@ #define UNREAL_VERSION_MAJOR 0 /* Minor version number (e.g.: 1 for Unreal3.2.1) */ -#define UNREAL_VERSION_MINOR 1 +#define UNREAL_VERSION_MINOR 3 /* Version suffix such as a beta marker or release candidate marker. (e.g.: -rcX for unrealircd-3.2.9-rcX) */ -#define UNREAL_VERSION_SUFFIX ".1" +#define UNREAL_VERSION_SUFFIX "" #endif diff --git a/src/Makefile.in b/src/Makefile.in index 62d4388..5895561 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -21,10 +21,10 @@ CC = "==== DO NOT RUN MAKE FROM THIS DIRECTORY ====" -OBJS=dns.o auth.o channel.o crule.o dbuf.o \ - fdlist.o hash.o ircd.o ircsprintf.o list.o \ +OBJS=ircd_vars.o dns.o auth.o channel.o crule.o dbuf.o \ + fdlist.o hash.o ircsprintf.o list.o \ match.o modules.o parse.o mempool.o operclass.o \ - conf_preprocessor.o conf.o debug.o dispatch.o \ + conf_preprocessor.o conf.o proc_io_server.o debug.o dispatch.o \ misc.o serv.o aliases.o socket.o \ tls.o user.o scache.o send.o support.o \ version.o whowas.o random.o api-usermode.o api-channelmode.o \ @@ -62,22 +62,22 @@ all: build build: # Force build of 'ircd', before we start building any modules: - $(MAKE) ircd + $(MAKE) ircd unrealircdctl $(MAKE) mods custommodule: +cd modules/third; $(MAKE) MODULEFILE=$(MODULEFILE) 'EXLIBS=$(EXLIBS)' custommodule -ircd: $(OBJS) - $(CC) $(CFLAGS) $(BINCFLAGS) $(CRYPTOLIB) -o ircd $(OBJS) $(LDFLAGS) $(BINLDFLAGS) $(IRCDLIBS) $(CRYPTOLIB) +ircd: $(OBJS) ircd.o + $(CC) $(CFLAGS) $(BINCFLAGS) $(CRYPTOLIB) -o ircd ircd.o $(OBJS) $(LDFLAGS) $(BINLDFLAGS) $(IRCDLIBS) $(CRYPTOLIB) + +unrealircdctl: $(OBJS) unrealircdctl.o proc_io_client.o + $(CC) $(CFLAGS) $(BINCFLAGS) $(CRYPTOLIB) -o unrealircdctl unrealircdctl.o proc_io_client.o $(OBJS) $(LDFLAGS) $(BINLDFLAGS) $(IRCDLIBS) $(CRYPTOLIB) mods: @if [ ! -r include ] ; then \ ln -s ../include include; \ fi - @if [ ! -r modules ] ; then \ - echo "You havent done cvs update -P -d"; \ - fi +cd modules; $(MAKE) all version.c: version.c.SH diff --git a/src/api-command.c b/src/api-command.c index f61e2bf..065a4eb 100644 --- a/src/api-command.c +++ b/src/api-command.c @@ -89,7 +89,7 @@ static Command *CommandAddInternal(Module *module, const char *cmd, CmdFunc func Command *command = NULL; RealCommand *c; - if (find_command_simple(cmd)) + if ((c = find_command(cmd, flags)) && (c->flags == flags)) { if (module) module->errorcode = MODERR_EXISTS; @@ -253,15 +253,26 @@ static RealCommand *add_Command_backend(const char *cmd) RealCommand *find_command(const char *cmd, int flags) { RealCommand *p; - for (p = CommandHash[toupper(*cmd)]; p; p = p->next) { - if ((flags & CMD_UNREGISTERED) && !(p->flags & CMD_UNREGISTERED)) - continue; - if ((flags & CMD_SHUN) && !(p->flags & CMD_SHUN)) - continue; - if ((flags & CMD_VIRUS) && !(p->flags & CMD_VIRUS)) - continue; - if ((flags & CMD_ALIAS) && !(p->flags & CMD_ALIAS)) - continue; + for (p = CommandHash[toupper(*cmd)]; p; p = p->next) + { + if (flags & CMD_CONTROL) + { + if (!(p->flags & CMD_CONTROL)) + continue; + } else + { + if ((flags & CMD_UNREGISTERED) && !(p->flags & CMD_UNREGISTERED)) + continue; + if ((flags & CMD_SHUN) && !(p->flags & CMD_SHUN)) + continue; + if ((flags & CMD_VIRUS) && !(p->flags & CMD_VIRUS)) + continue; + if ((flags & CMD_ALIAS) && !(p->flags & CMD_ALIAS)) + continue; + if (p->flags & CMD_CONTROL) + continue; /* important to also filter it this way ;) */ + } + if (!strcasecmp(p->cmd, cmd)) return p; } diff --git a/src/api-event.c b/src/api-event.c index 799edb7..1ad24f6 100644 --- a/src/api-event.c +++ b/src/api-event.c @@ -27,9 +27,6 @@ ID_Copyright("(C) Carsten Munk 2001"); MODVAR Event *events = NULL; -extern EVENT(unrealdns_removeoldrecords); -extern EVENT(unrealdb_expire_secret_cache); - /** Add an event, a function that will run at regular intervals. * @param module Module that this event belongs to * @param name Name of the event @@ -211,18 +208,3 @@ void DoEvents(void) CleanupEvents(); } - -void SetupEvents(void) -{ - /* Start events */ - EventAdd(NULL, "tunefile", save_tunefile, NULL, 300*1000, 0); - EventAdd(NULL, "garbage", garbage_collect, NULL, GARBAGE_COLLECT_EVERY*1000, 0); - EventAdd(NULL, "loop", loop_event, NULL, 1000, 0); - EventAdd(NULL, "unrealdns_removeoldrecords", unrealdns_removeoldrecords, NULL, 15000, 0); - EventAdd(NULL, "check_pings", check_pings, NULL, 1000, 0); - EventAdd(NULL, "check_deadsockets", check_deadsockets, NULL, 1000, 0); - EventAdd(NULL, "handshake_timeout", handshake_timeout, NULL, 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); - EventAdd(NULL, "throttling_check_expire", throttling_check_expire, NULL, 1000, 0); -} diff --git a/src/conf.c b/src/conf.c index f3d78df..24c551e 100644 --- a/src/conf.c +++ b/src/conf.c @@ -199,8 +199,8 @@ void free_tls_options(TLSOptions *tlsoptions); * Config parser (IRCd) */ int config_read_file(const char *filename, const char *display_name); -void config_rehash(); -int config_run_blocks(); +void config_rehash(void); +int config_run_blocks(void); int config_test_blocks(); /* @@ -464,10 +464,15 @@ int config_parse_flood_generic(const char *str, Configuration *conf, char *block long config_checkval(const char *orig, unsigned short flags) { - char *value = raw_strdup(orig); + char *value; char *text; long ret = 0; + /* Handle empty strings early, since we use +1 later in the code etc. */ + if (BadPtr(orig)) + return 0; + + value = raw_strdup(orig); if (flags == CFG_YESNO) { for (text = value; *text; text++) { if (!isalnum(*text)) @@ -1586,7 +1591,7 @@ ConfigCommand *config_binary_search(const char *cmd) { return NULL; } -void free_iConf(Configuration *i) +void free_iConf(Configuration *i) { FloodSettings *f, *f_next; @@ -1645,6 +1650,7 @@ void config_setdefaultsettings(Configuration *i) safe_strdup(i->oper_snomask, OPER_SNOMASKS); i->server_notice_colors = 1; + i->server_notice_show_event = 1; i->ident_read_timeout = 7; i->ident_connect_timeout = 3; i->ban_version_tkl_time = 86400; /* 1d */ @@ -1694,6 +1700,7 @@ void config_setdefaultsettings(Configuration *i) /* - known-users */ config_parse_flood_generic("3:60", i, "known-users", FLD_NICK); /* NICK flood protection: max 3 per 60s */ config_parse_flood_generic("3:90", i, "known-users", FLD_JOIN); /* JOIN flood protection: max 3 per 90s */ + config_parse_flood_generic("3:90", i, "known-users", FLD_VHOST); /* MODE -x flood protection: max 3 per 90s */ config_parse_flood_generic("4:120", i, "known-users", FLD_AWAY); /* AWAY flood protection: max 4 per 120s */ config_parse_flood_generic("4:60", i, "known-users", FLD_INVITE); /* INVITE flood protection: max 4 per 60s */ config_parse_flood_generic("4:120", i, "known-users", FLD_KNOCK); /* KNOCK protection: max 4 per 120s */ @@ -1702,6 +1709,7 @@ void config_setdefaultsettings(Configuration *i) /* - unknown-users */ config_parse_flood_generic("2:60", i, "unknown-users", FLD_NICK); /* NICK flood protection: max 2 per 60s */ config_parse_flood_generic("2:90", i, "unknown-users", FLD_JOIN); /* JOIN flood protection: max 2 per 90s */ + config_parse_flood_generic("2:90", i, "unknown-users", FLD_VHOST); /* MODE -x flood protection: max 2 per 90s */ config_parse_flood_generic("4:120", i, "unknown-users", FLD_AWAY); /* AWAY flood protection: max 4 per 120s */ config_parse_flood_generic("2:60", i, "unknown-users", FLD_INVITE); /* INVITE flood protection: max 2 per 60s */ config_parse_flood_generic("2:120", i, "unknown-users", FLD_KNOCK); /* KNOCK protection: max 2 per 120s */ @@ -2411,7 +2419,8 @@ void config_rehash() } for (listen_ptr = conf_listen; listen_ptr; listen_ptr = listen_ptr->next) { - listen_ptr->flag.temporary = 1; + if (!(listen_ptr->options & LISTENER_CONTROL)) + listen_ptr->flag.temporary = 1; } for (tld_ptr = conf_tld; tld_ptr; tld_ptr = (ConfigItem_tld *) next) { @@ -2642,152 +2651,87 @@ void config_switchover(void) log_blocks_switchover(); } -int config_run_blocks() +/** Priority of config blocks during CONFIG_TEST stage */ +static const char *config_test_priority_blocks[] = +{ + "me", + "secret", + "log", /* "log" needs to be before "set" in CONFIG_TEST */ + "set", + "class", +}; + +/** Priority of config blocks during CONFIG_RUN stage */ +static const char *config_run_priority_blocks[] = +{ + "me", + "secret", + "set", + "log", /* "log" needs to be after "set" in CONFIG_RUN */ + "class", +}; + +int config_test_blocks() { ConfigEntry *ce; ConfigFile *cfptr; ConfigCommand *cc; int errors = 0; + int i; Hook *h; - ConfigItem_allow *allow; - /* Stage 1: set block first */ - for (cfptr = conf; cfptr; cfptr = cfptr->next) + invalid_snomasks_encountered = 0; + + /* Stage 1: first the priority blocks, in the order as specified + * in config_test_priority_blocks[] + */ + for (i=0; i < ARRAY_SIZEOF(config_test_priority_blocks); i++) { - if (config_verbose > 1) - config_status("Running %s", cfptr->filename); - for (ce = cfptr->items; ce; ce = ce->next) + const char *config_block = config_test_priority_blocks[i]; + cc = config_binary_search(config_block); + if (!cc) + abort(); /* internal fuckup */ + for (cfptr = conf; cfptr; cfptr = cfptr->next) { - if (!strcmp(ce->name, "set")) + if (config_verbose > 1) + config_status("Running %s", cfptr->filename); + for (ce = cfptr->items; ce; ce = ce->next) { - if (_conf_set(cfptr, ce) < 0) - errors++; - } - } - } - - /* Stage 2: now class blocks */ - for (cfptr = conf; cfptr; cfptr = cfptr->next) - { - if (config_verbose > 1) - config_status("Running %s", cfptr->filename); - for (ce = cfptr->items; ce; ce = ce->next) - { - if (!strcmp(ce->name, "class")) - { - if (_conf_class(cfptr, ce) < 0) - errors++; - } - } - } - - /* Stage 3: now all the rest */ - for (cfptr = conf; cfptr; cfptr = cfptr->next) - { - if (config_verbose > 1) - config_status("Running %s", cfptr->filename); - for (ce = cfptr->items; ce; ce = ce->next) - { - /* These are already processed above (set, class) - * or via config_test_blocks() (secret). - */ - if (!strcmp(ce->name, "set") || - !strcmp(ce->name, "class") || - !strcmp(ce->name, "secret")) - { - continue; - } - - if ((cc = config_binary_search(ce->name))) { - if ((cc->conffunc) && (cc->conffunc(cfptr, ce) < 0)) - errors++; - } - else - { - int value; - for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) + if (!strcmp(ce->name, config_block)) { - value = (*(h->func.intfunc))(cfptr,ce,CONFIG_MAIN); - if (value == 1) - break; + int n = cc->testfunc(cfptr, ce); + errors += n; + if (!strcmp(config_block, "secret") && (n == 0)) + { + /* Yeah special case: secret { } blocks we run + * immediately here. + */ + _conf_secret(cfptr, ce); + } } } } } - close_unbound_listeners(); - listen_cleanup(); - close_unbound_listeners(); - loop.do_bancheck = 1; - config_switchover(); - update_throttling_timer_settings(); - - /* initialize conf_files with defaults if the block isn't set: */ - if (!conf_files) - _conf_files(NULL, NULL); - - if (errors > 0) - { - config_error("%i fatal errors encountered", errors); - } - return (errors > 0 ? -1 : 1); -} - - -int config_test_blocks() -{ - ConfigEntry *ce; - ConfigFile *cfptr; - ConfigCommand *cc; - int errors = 0; - Hook *h; - - invalid_snomasks_encountered = 0; - - /* First, all the log { } blocks everywhere */ + /* Stage 2: now all the other config blocks */ for (cfptr = conf; cfptr; cfptr = cfptr->next) { if (config_verbose > 1) - config_status("Testing %s", cfptr->filename); - /* First test and run the log { } blocks */ + config_status("Running %s", cfptr->filename); for (ce = cfptr->items; ce; ce = ce->next) { - if (!strcmp(ce->name, "log")) - errors += config_test_log(cfptr, ce); - } - } - - for (cfptr = conf; cfptr; cfptr = cfptr->next) - { - if (config_verbose > 1) - config_status("Testing %s", cfptr->filename); - /* First test and run the secret { } blocks */ - for (ce = cfptr->items; ce; ce = ce->next) - { - if (!strcmp(ce->name, "secret")) + char skip = 0; + for (i=0; i < ARRAY_SIZEOF(config_test_priority_blocks); i++) { - int n = _test_secret(cfptr, ce); - errors += n; - if (n == 0) - _conf_secret(cfptr, ce); + if (!strcmp(ce->name, config_test_priority_blocks[i])) + { + skip = 1; + break; + } } - } - /* First test the set { } block */ - for (ce = cfptr->items; ce; ce = ce->next) - { - if (!strcmp(ce->name, "set")) - errors += _test_set(cfptr, ce); - } - /* Now test all the rest */ - for (ce = cfptr->items; ce; ce = ce->next) - { - /* These are already processed, so skip them here.. */ - if (!strcmp(ce->name, "secret") || - !strcmp(ce->name, "set") || - !strcmp(ce->name, "log")) - { + if (skip) continue; - } + if ((cc = config_binary_search(ce->name))) { if (cc->testfunc) errors += (cc->testfunc(cfptr, ce)); @@ -2841,7 +2785,9 @@ int config_test_blocks() } } } + errors += config_post_test(); + if (errors > 0) { config_error("%i errors encountered", errors); @@ -2856,6 +2802,96 @@ int config_test_blocks() return (errors > 0 ? -1 : 1); } +int config_run_blocks(void) +{ + ConfigEntry *ce; + ConfigFile *cfptr; + ConfigCommand *cc; + int errors = 0; + int i; + Hook *h; + ConfigItem_allow *allow; + + /* Stage 1: first the priority blocks, in the order as specified + * in config_run_priority_blocks[] + */ + for (i=0; i < ARRAY_SIZEOF(config_run_priority_blocks); i++) + { + const char *config_block = config_run_priority_blocks[i]; + cc = config_binary_search(config_block); + if (!cc) + abort(); /* internal fuckup */ + if (!strcmp(config_block, "secret")) + continue; /* yeah special case, we already processed the run part in test for these */ + for (cfptr = conf; cfptr; cfptr = cfptr->next) + { + if (config_verbose > 1) + config_status("Running %s", cfptr->filename); + for (ce = cfptr->items; ce; ce = ce->next) + { + if (!strcmp(ce->name, config_block)) + { + if (cc->conffunc(cfptr, ce) < 0) + errors++; + } + } + } + } + + /* Stage 2: now all the other config blocks */ + for (cfptr = conf; cfptr; cfptr = cfptr->next) + { + if (config_verbose > 1) + config_status("Running %s", cfptr->filename); + for (ce = cfptr->items; ce; ce = ce->next) + { + char skip = 0; + for (i=0; i < ARRAY_SIZEOF(config_run_priority_blocks); i++) + { + if (!strcmp(ce->name, config_run_priority_blocks[i])) + { + skip = 1; + break; + } + } + if (skip) + continue; + + if ((cc = config_binary_search(ce->name))) { + if ((cc->conffunc) && (cc->conffunc(cfptr, ce) < 0)) + errors++; + } + else + { + int value; + for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) + { + value = (*(h->func.intfunc))(cfptr,ce,CONFIG_MAIN); + if (value == 1) + break; + } + } + } + } + + close_unbound_listeners(); + listen_cleanup(); + close_unbound_listeners(); + loop.do_bancheck = 1; + config_switchover(); + update_throttling_timer_settings(); + + /* initialize conf_files with defaults if the block isn't set: */ + if (!conf_files) + _conf_files(NULL, NULL); + + if (errors > 0) + { + config_error("%i fatal errors encountered", errors); + } + return (errors > 0 ? -1 : 1); +} + /* * Service functions */ @@ -2935,7 +2971,7 @@ int count_oper_sessions(const char *name) return count; } -ConfigItem_listen *find_listen(const char *ipmask, int port, int ipv6) +ConfigItem_listen *find_listen(const char *ipmask, int port, SocketType socket_type) { ConfigItem_listen *e; @@ -2943,8 +2979,19 @@ ConfigItem_listen *find_listen(const char *ipmask, int port, int ipv6) return NULL; for (e = conf_listen; e; e = e->next) - if ((e->ipv6 == ipv6) && (e->port == port) && !strcmp(e->ip, ipmask)) - return e; + { + if (e->socket_type != socket_type) + continue; + if (e->socket_type == SOCKET_TYPE_UNIX) + { + if (!strcmp(e->file, ipmask)) + return e; + } else + { + if ((e->socket_type == socket_type) && (e->port == port) && !strcmp(e->ip, ipmask)) + return e; + } + } return NULL; } @@ -3889,7 +3936,9 @@ int _conf_oper(ConfigFile *conf, ConfigEntry *ce) oper = safe_alloc(sizeof(ConfigItem_oper)); safe_strdup(oper->name, ce->value); - oper->server_notice_colors = tempiConf.server_notice_colors; /* default */ + /* Inherit some defaults: */ + oper->server_notice_colors = tempiConf.server_notice_colors; + oper->server_notice_show_event = tempiConf.server_notice_show_event; for (cep = ce->items; cep; cep = cep->next) { @@ -3937,6 +3986,10 @@ int _conf_oper(ConfigFile *conf, ConfigEntry *ce) { oper->server_notice_colors = config_checkval(cep->value, CFG_YESNO); } + else if (!strcmp(cep->name, "server-notice-show-event")) + { + oper->server_notice_show_event = config_checkval(cep->value, CFG_YESNO); + } else if (!strcmp(cep->name, "modes")) { oper->modes = set_usermode(cep->value); @@ -4069,6 +4122,9 @@ int _test_oper(ConfigFile *conf, ConfigEntry *ce) else if (!strcmp(cep->name, "server-notice-colors")) { } + else if (!strcmp(cep->name, "server-notice-show-event")) + { + } /* oper::modes */ else if (!strcmp(cep->name, "modes")) { @@ -4777,6 +4833,7 @@ int _conf_listen(ConfigFile *conf, ConfigEntry *ce) ConfigEntry *cepp; ConfigEntry *tlsconfig = NULL; ConfigItem_listen *listen = NULL; + char *file = NULL; char *ip = NULL; int start=0, end=0, port, isnew; int tmpflags =0; @@ -4784,6 +4841,10 @@ int _conf_listen(ConfigFile *conf, ConfigEntry *ce) for (cep = ce->items; cep; cep = cep->next) { + if (!strcmp(cep->name, "file")) + { + file = cep->value; + } else if (!strcmp(cep->name, "ip")) { ip = cep->value; @@ -4825,18 +4886,44 @@ int _conf_listen(ConfigFile *conf, ConfigEntry *ce) } } } + + /* UNIX domain socket code */ + if (file) + { + if (!(listen = find_listen(file, 0, SOCKET_TYPE_UNIX))) + { + listen = safe_alloc(sizeof(ConfigItem_listen)); + safe_strdup(listen->file, file); + listen->socket_type = SOCKET_TYPE_UNIX; + listen->fd = -1; + isnew = 1; + } else { + isnew = 0; + } + + if (listen->options & LISTENER_BOUND) + tmpflags |= LISTENER_BOUND; + + listen->options = tmpflags; + if (isnew) + AddListItem(listen, conf_listen); + listen->flag.temporary = 0; + + return 1; + } + for (port = start; port <= end; port++) { /* First deal with IPv4 */ if (!strchr(ip, ':')) { - if (!(listen = find_listen(ip, port, 0))) + if (!(listen = find_listen(ip, port, SOCKET_TYPE_IPV4))) { listen = safe_alloc(sizeof(ConfigItem_listen)); safe_strdup(listen->ip, ip); listen->port = port; listen->fd = -1; - listen->ipv6 = 0; + listen->socket_type = SOCKET_TYPE_IPV4; isnew = 1; } else isnew = 0; @@ -4915,13 +5002,13 @@ int _conf_listen(ConfigFile *conf, ConfigEntry *ce) { if (strchr(ip, ':') || (*ip == '*')) { - if (!(listen = find_listen(ip, port, 1))) + if (!(listen = find_listen(ip, port, SOCKET_TYPE_IPV6))) { listen = safe_alloc(sizeof(ConfigItem_listen)); safe_strdup(listen->ip, ip); listen->port = port; listen->fd = -1; - listen->ipv6 = 1; + listen->socket_type = SOCKET_TYPE_IPV6; isnew = 1; } else isnew = 0; @@ -5002,7 +5089,8 @@ int _test_listen(ConfigFile *conf, ConfigEntry *ce) ConfigEntry *cep; ConfigEntry *cepp; int errors = 0; - char has_ip = 0, has_port = 0, has_options = 0, port_6667 = 0; + char has_file = 0, has_ip = 0, has_port = 0, has_options = 0, port_6667 = 0; + char *file = NULL; char *ip = NULL; Hook *h; @@ -5117,6 +5205,11 @@ int _test_listen(ConfigFile *conf, ConfigEntry *ce) } continue; /* always */ } else + if (!strcmp(cep->name, "file")) + { + has_file = 1; + file = cep->value; + } else if (!strcmp(cep->name, "ip")) { has_ip = 1; @@ -5189,18 +5282,32 @@ int _test_listen(ConfigFile *conf, ConfigEntry *ce) } } - if (!has_ip) + if (has_file) { - config_error("%s:%d: listen block requires an listen::ip", - ce->file->filename, ce->line_number); - errors++; - } + if (has_ip || has_port) + { + config_error("%s:%d: listen block should either have a 'file' (for *NIX domain socket), " + "OR have an 'ip' and 'port' (for IPv4/IPv6). You cannot combine both in one listen block.", + ce->file->filename, ce->line_number); + errors++; + } else { + // TODO: check if file can be created fresh etc. + } + } else + { + if (!has_ip) + { + config_error("%s:%d: listen block requires an listen::ip", + ce->file->filename, ce->line_number); + errors++; + } - if (!has_port) - { - config_error("%s:%d: listen block requires an listen::port", - ce->file->filename, ce->line_number); - errors++; + if (!has_port) + { + config_error("%s:%d: listen block requires an listen::port", + ce->file->filename, ce->line_number); + errors++; + } } if (port_6667) @@ -7167,6 +7274,9 @@ int _conf_set(ConfigFile *conf, ConfigEntry *ce) else if (!strcmp(cep->name, "server-notice-colors")) { tempiConf.server_notice_colors = config_checkval(cep->value, CFG_YESNO); } + else if (!strcmp(cep->name, "server-notice-show-event")) { + tempiConf.server_notice_show_event = config_checkval(cep->value, CFG_YESNO); + } else if (!strcmp(cep->name, "level-on-join")) { const char *res = channellevel_to_string(cep->value); /* 'halfop', etc */ if (!res) @@ -7380,6 +7490,10 @@ int _conf_set(ConfigFile *conf, ConfigEntry *ce) { config_parse_flood_generic(ceppp->value, &tempiConf, cepp->name, FLD_NICK); } + else if (!strcmp(ceppp->name, "vhost-flood")) + { + config_parse_flood_generic(ceppp->value, &tempiConf, cepp->name, FLD_VHOST); + } else if (!strcmp(ceppp->name, "join-flood")) { config_parse_flood_generic(ceppp->value, &tempiConf, cepp->name, FLD_JOIN); @@ -7847,6 +7961,9 @@ int _test_set(ConfigFile *conf, ConfigEntry *ce) else if (!strcmp(cep->name, "server-notice-colors")) { CheckNull(cep); } + else if (!strcmp(cep->name, "server-notice-show-event")) { + CheckNull(cep); + } else if (!strcmp(cep->name, "level-on-join")) { CheckNull(cep); CheckDuplicate(cep, level_on_join, "level-on-join"); @@ -8340,6 +8457,19 @@ int _test_set(ConfigFile *conf, ConfigEntry *ce) errors++; } } + else if (!strcmp(ceppp->name, "vhost-flood")) + { + int cnt, period; + CheckNull(ceppp); + if (!config_parse_flood(ceppp->value, &cnt, &period) || + (cnt < 1) || (cnt > 255) || (period < 5)) + { + config_error("%s:%i: set::anti-flood::vhost-flood error. Syntax is ':' (eg 5:60), " + "count should be 1-255, period should be greater than 4", + ceppp->file->filename, ceppp->line_number); + errors++; + } + } else if (!strcmp(ceppp->name, "join-flood")) { int cnt, period; @@ -9191,14 +9321,22 @@ void start_listeners(void) log_data_string("listen_ip", listener->ip), log_data_integer("listen_port", listener->port)); } else { - if (listener->ipv6) - snprintf(boundmsg_ipv6+strlen(boundmsg_ipv6), sizeof(boundmsg_ipv6)-strlen(boundmsg_ipv6), - "%s:%d%s, ", listener->ip, listener->port, - listener->options & LISTENER_TLS ? "(TLS)" : ""); - else - snprintf(boundmsg_ipv4+strlen(boundmsg_ipv4), sizeof(boundmsg_ipv4)-strlen(boundmsg_ipv4), - "%s:%d%s, ", listener->ip, listener->port, - listener->options & LISTENER_TLS ? "(TLS)" : ""); + switch (listener->socket_type) + { + case SOCKET_TYPE_IPV4: + snprintf(boundmsg_ipv4+strlen(boundmsg_ipv4), sizeof(boundmsg_ipv4)-strlen(boundmsg_ipv4), + "%s:%d%s, ", listener->ip, listener->port, + listener->options & LISTENER_TLS ? "(TLS)" : ""); + break; + case SOCKET_TYPE_IPV6: + snprintf(boundmsg_ipv6+strlen(boundmsg_ipv6), sizeof(boundmsg_ipv6)-strlen(boundmsg_ipv6), + "%s:%d%s, ", listener->ip, listener->port, + listener->options & LISTENER_TLS ? "(TLS)" : ""); + break; + // TODO: show unix domain sockets ;) + default: + break; + } } } } @@ -9268,6 +9406,7 @@ void config_run(void) { extcmodes_check_for_changes(); start_listeners(); + add_proc_io_server(); free_all_config_resources(); } @@ -10499,13 +10638,6 @@ void resource_download_complete(const char *url, const char *file, const char *e safe_strdup(wce->ce->value, rs->file); // now information of url is lost, hm!! } } - - /* If rehashing, check if we are done. - * If booting (not rehashing), this is done from the - * startup loop where it also checks is_config_read_finished(). - */ - if (loop.rehashing && is_config_read_finished()) - rehash_internal(loop.rehash_save_client); } /** Request to REHASH the configuration file. @@ -10540,13 +10672,16 @@ void request_rehash(Client *client) int rehash_internal(Client *client) { + int failure; + /* Log it here if it is by a signal */ if (client == NULL) unreal_log(ULOG_INFO, "config", "CONFIG_RELOAD", client, "Rehashing server configuration file [./unrealircd rehash]"); - loop.rehashing = 1; /* double checking.. */ + loop.rehashing = 2; /* now doing the actual rehash */ - if (config_test() == 0) + failure = config_test(); + if (failure == 0) config_run(); /* TODO: uh.. are we supposed to do all this for a failed rehash too? maybe some but not all? */ reread_motdsandrules(); @@ -10557,8 +10692,11 @@ int rehash_internal(Client *client) // unload_all_unused_moddata(); -- this will crash umodes_check_for_changes(); charsys_check_for_changes(); + + /* Clear everything now that we are done */ loop.rehashing = 0; remote_rehash_client = NULL; + procio_post_rehash(failure); return 1; } diff --git a/src/crashreport.c b/src/crashreport.c index 270c363..f3ac0f5 100644 --- a/src/crashreport.c +++ b/src/crashreport.c @@ -649,6 +649,13 @@ int crashreport_send(char *fname) if ((n < 0) || strncmp(buf, "HTTP/1.1 100", 12)) { printf("Error transmitting bug report (stage II, n=%d)\n", n); + if (!strncmp(buf, "HTTP/1.1 403", 12)) + { + printf("Your crash report was rejected automatically.\n" + "This normally means your UnrealIRCd version is too old and unsupported.\n" + "Chances are that your crash issue is already fixed in a later release.\n" + "Check https://www.unrealircd.org/ for latest releases!\n"); + } return 0; } diff --git a/src/dbuf.c b/src/dbuf.c index 23786d5..b1ba1ab 100644 --- a/src/dbuf.c +++ b/src/dbuf.c @@ -58,6 +58,7 @@ static void dbuf_free(dbufbuf *ptr) void dbuf_queue_init(dbuf *dyn) { + memset(dyn, 0, sizeof(dbuf)); INIT_LIST_HEAD(&dyn->dbuf_list); } diff --git a/src/dns.c b/src/dns.c index d8cb660..4335efe 100644 --- a/src/dns.c +++ b/src/dns.c @@ -113,7 +113,7 @@ static int unrealdns_sock_create_cb(ares_socket_t fd, int type, void *data) return ARES_SUCCESS; } -static EVENT(unrealdns_timeout) +EVENT(unrealdns_timeout) { ares_process_fd(resolver_channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); } diff --git a/src/ircd.c b/src/ircd.c index dbb5554..7fcd185 100644 --- a/src/ircd.c +++ b/src/ircd.c @@ -25,209 +25,10 @@ char *malloc_options = "h" MALLOC_FLAGS_EXTRA; #endif -#ifndef _WIN32 -extern char unreallogo[]; -#endif -int SVSNOOP = 0; -extern MODVAR char *buildid; -time_t timeofday = 0; -struct timeval timeofday_tv; -int tainted = 0; -LoopStruct loop; -#ifndef _WIN32 -uid_t irc_uid = 0; -gid_t irc_gid = 0; -#endif - -MODVAR IRCCounts irccounts; -MODVAR Client me; /* That's me */ -MODVAR char *me_hash; -extern char backupbuf[8192]; -#ifdef _WIN32 -extern SERVICE_STATUS_HANDLE IRCDStatusHandle; -extern SERVICE_STATUS IRCDStatus; -#endif - -MODVAR unsigned char conf_debuglevel = 0; - +/* Forward declarations */ void server_reboot(const char *); void restart(const char *); static void open_debugfile(), setup_signals(); -extern void init_glines(void); -extern void tkl_init(void); -extern void process_clients(void); -extern void unrealdb_test(void); - -#ifndef _WIN32 -MODVAR char **myargv; -#else -LPCSTR cmdLine; -#endif -char *configfile = NULL; /* Server configuration file */ -int debuglevel = 0; /* Server debug level */ -int bootopt = 0; /* Server boot option flags */ -char *debugmode = ""; /* -"- -"- -"- */ -char *sbrk0; /* initial sbrk(0) */ -static int dorehash = 0, dorestart = 0, doreloadcert = 0; -MODVAR int booted = FALSE; - -void s_die() -{ -#ifdef _WIN32 - Client *client; - if (!IsService) - { - loop.terminating = 1; - unload_all_modules(); - - list_for_each_entry(client, &lclient_list, lclient_node) - (void) send_queued(client); - - exit(-1); - } - else { - SERVICE_STATUS status; - SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); - SC_HANDLE hService = OpenService(hSCManager, "UnrealIRCd", SERVICE_STOP); - ControlService(hService, SERVICE_CONTROL_STOP, &status); - } -#else - loop.terminating = 1; - unload_all_modules(); - unlink(conf_files ? conf_files->pid_file : IRCD_PIDFILE); - exit(0); -#endif -} - -#ifndef _WIN32 -static void s_rehash() -{ - struct sigaction act; - dorehash = 1; - act.sa_handler = s_rehash; - act.sa_flags = 0; - (void)sigemptyset(&act.sa_mask); - (void)sigaddset(&act.sa_mask, SIGHUP); - (void)sigaction(SIGHUP, &act, NULL); -} - -static void s_reloadcert() -{ - struct sigaction act; - doreloadcert = 1; - act.sa_handler = s_reloadcert; - act.sa_flags = 0; - (void)sigemptyset(&act.sa_mask); - (void)sigaddset(&act.sa_mask, SIGUSR1); - (void)sigaction(SIGUSR1, &act, NULL); -} -#endif // #ifndef _WIN32 - -void restart(const char *mesg) -{ - server_reboot(mesg); -} - -void s_restart() -{ - dorestart = 1; -#if 0 - static int restarting = 0; - - if (restarting == 0) { - /* - * Send (or attempt to) a dying scream to oper if present - */ - - restarting = 1; - server_reboot("SIGINT"); - } -#endif -} - - -#ifndef _WIN32 -/** Signal handler for signals which we ignore, - * like SIGPIPE ("Broken pipe") and SIGWINCH (terminal window changed) etc. - */ -void ignore_this_signal() -{ - struct sigaction act; - - act.sa_handler = ignore_this_signal; - act.sa_flags = 0; - (void)sigemptyset(&act.sa_mask); - (void)sigaddset(&act.sa_mask, SIGALRM); - (void)sigaddset(&act.sa_mask, SIGPIPE); - (void)sigaction(SIGALRM, &act, (struct sigaction *)NULL); - (void)sigaction(SIGPIPE, &act, (struct sigaction *)NULL); -#ifdef SIGWINCH - (void)sigaddset(&act.sa_mask, SIGWINCH); - (void)sigaction(SIGWINCH, &act, (struct sigaction *)NULL); -#endif -} -#endif /* #ifndef _WIN32 */ - - -void server_reboot(const char *mesg) -{ - int i; - Client *client; - unreal_log(ULOG_INFO, "main", "UNREALIRCD_RESTARTING", NULL, - "Restarting server: $reason", - log_data_string("reason", mesg)); - - list_for_each_entry(client, &lclient_list, lclient_node) - (void) send_queued(client); - - /* - * ** fd 0 must be 'preserved' if either the -d or -i options have - * ** been passed to us before restarting. - */ -#ifdef HAVE_SYSLOG - (void)closelog(); -#endif -#ifndef _WIN32 - for (i = 3; i < MAXCONNECTIONS; i++) - (void)close(i); - if (!(bootopt & (BOOT_TTY | BOOT_DEBUG))) - (void)close(2); - (void)close(1); - (void)close(0); - (void)execv(MYNAME, myargv); -#else - close_connections(); - if (!IsService) - { - CleanUp(); - WinExec(cmdLine, SW_SHOWDEFAULT); - } -#endif - unload_all_modules(); -#ifdef _WIN32 - if (IsService) - { - SERVICE_STATUS status; - PROCESS_INFORMATION pi; - STARTUPINFO si; - char fname[MAX_PATH]; - memset(&status, 0, sizeof(status)); - memset(&si, 0, sizeof(si)); - IRCDStatus.dwCurrentState = SERVICE_STOP_PENDING; - SetServiceStatus(IRCDStatusHandle, &IRCDStatus); - GetModuleFileName(GetModuleHandle(NULL), fname, MAX_PATH); - CreateProcess(fname, "restartsvc", NULL, NULL, FALSE, - 0, NULL, NULL, &si, &pi); - IRCDStatus.dwCurrentState = SERVICE_STOPPED; - SetServiceStatus(IRCDStatusHandle, &IRCDStatus); - ExitProcess(0); - } - else -#endif - exit(-1); -} - -MODVAR char *areason; EVENT(loop_event) { @@ -557,58 +358,6 @@ void fix_timers(void) } -#ifndef _WIN32 -/* Generate 3 cloak keys and print to console */ -static void generate_cloakkeys() -{ - #define GENERATE_CLOAKKEY_LEN 80 /* Length of cloak keys to generate. */ - char keyBuf[GENERATE_CLOAKKEY_LEN + 1]; - int keyNum; - int charIndex; - - short has_upper; - short has_lower; - short has_num; - - fprintf(stderr, "Here are 3 random cloak keys that you can copy-paste to your configuration file:\n\n"); - - fprintf(stderr, "set {\n\tcloak-keys {\n"); - for (keyNum = 0; keyNum < 3; ++keyNum) - { - has_upper = 0; - has_lower = 0; - has_num = 0; - - for (charIndex = 0; charIndex < sizeof(keyBuf)-1; ++charIndex) - { - switch (getrandom8() % 3) - { - case 0: /* Uppercase. */ - keyBuf[charIndex] = (char)('A' + (getrandom8() % ('Z' - 'A'))); - has_upper = 1; - break; - case 1: /* Lowercase. */ - keyBuf[charIndex] = (char)('a' + (getrandom8() % ('z' - 'a'))); - has_lower = 1; - break; - case 2: /* Digit. */ - keyBuf[charIndex] = (char)('0' + (getrandom8() % ('9' - '0'))); - has_num = 1; - break; - } - } - keyBuf[sizeof(keyBuf)-1] = '\0'; - - if (has_upper && has_lower && has_num) - fprintf(stderr, "\t\t\"%s\";\n", keyBuf); - else - /* Try again. For this reason, keyNum must be signed. */ - keyNum--; - } - fprintf(stderr, "\t}\n}\n\n"); -} -#endif - /* MY tdiff... because 'double' sucks. * This should work until 2038, and very likely after that as well * because 'long' should be 64 bit on all systems by then... -- Syzop @@ -677,30 +426,19 @@ void detect_timeshift_and_warn(void) oldtimeofday = timeofday; } -/** Check if at least 'minimum' seconds passed by since last run. - * @param tv_old Pointer to a timeval struct to keep track of things. - * @param minimum The time specified in milliseconds (eg: 1000 for 1 second) - * @returns When 'minimum' msec passed 1 is returned and the time is reset, otherwise 0 is returned. - */ -int minimum_msec_since_last_run(struct timeval *tv_old, long minimum) +void SetupEvents(void) { - long v; - - if (tv_old->tv_sec == 0) - { - /* First call ever */ - tv_old->tv_sec = timeofday_tv.tv_sec; - tv_old->tv_usec = timeofday_tv.tv_usec; - return 0; - } - v = ((timeofday_tv.tv_sec - tv_old->tv_sec) * 1000) + ((timeofday_tv.tv_usec - tv_old->tv_usec)/1000); - if (v >= minimum) - { - tv_old->tv_sec = timeofday_tv.tv_sec; - tv_old->tv_usec = timeofday_tv.tv_usec; - return 1; - } - return 0; + /* Start events */ + EventAdd(NULL, "tunefile", save_tunefile, NULL, 300*1000, 0); + EventAdd(NULL, "garbage", garbage_collect, NULL, GARBAGE_COLLECT_EVERY*1000, 0); + EventAdd(NULL, "loop", loop_event, NULL, 1000, 0); + EventAdd(NULL, "unrealdns_removeoldrecords", unrealdns_removeoldrecords, NULL, 15000, 0); + EventAdd(NULL, "check_pings", check_pings, NULL, 1000, 0); + EventAdd(NULL, "check_deadsockets", check_deadsockets, NULL, 1000, 0); + EventAdd(NULL, "handshake_timeout", handshake_timeout, NULL, 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); + EventAdd(NULL, "throttling_check_expire", throttling_check_expire, NULL, 1000, 0); } /** The main function. This will call SocketLoop() once the server is ready. */ @@ -710,10 +448,7 @@ int main(int argc, char *argv[]) int InitUnrealIRCd(int argc, char *argv[]) #endif { -#ifdef _WIN32 - WORD wVersionRequested = MAKEWORD(1, 1); - WSADATA wsaData; -#else +#ifndef _WIN32 uid_t uid, euid; gid_t gid, egid; #endif @@ -755,7 +490,6 @@ int InitUnrealIRCd(int argc, char *argv[]) SetErrorMode(SEM_FAILCRITICALERRORS); #endif #if !defined(_WIN32) && !defined(_AMIGA) - sbrk0 = (char *)sbrk((size_t)0); uid = getuid(); euid = geteuid(); gid = getgid(); @@ -784,7 +518,7 @@ int InitUnrealIRCd(int argc, char *argv[]) #ifndef _WIN32 (void)umask(077); /* better safe than sorry --SRB */ #else - WSAStartup(wVersionRequested, &wsaData); + init_winsock(); #endif setup_signals(); @@ -840,43 +574,6 @@ int InitUnrealIRCd(int argc, char *argv[]) safe_strdup(configfile, p); convert_to_absolute_path(&configfile, CONFDIR); break; -#ifndef _WIN32 - case 'P':{ - short type; - const char *result; - srandom(TStime()); - type = Auth_FindType(NULL, p); - if (type == -1) - { - type = AUTHTYPE_ARGON2; - } else { - p = *++argv; - argc--; - } - if (BadPtr(p)) - { -#ifndef _WIN32 - p = getpass("Enter password to hash: "); -#else - printf("ERROR: You should specify a password to hash"); - exit(1); -#endif - } - if ((type == AUTHTYPE_UNIXCRYPT) && (strlen(p) > 8)) - { - /* Hmmm.. is this warning really still true (and always) ?? */ - printf("WARNING: Password truncated to 8 characters due to 'crypt' algorithm. " - "You are suggested to use the 'argon2' algorithm instead."); - p[8] = '\0'; - } - if (!(result = Auth_Hash(type, p))) { - printf("Failed to generate password. Deprecated method? Try 'argon2' instead.\n"); - exit(0); - } - printf("Encrypted password is: %s\n", result); - exit(0); - } -#endif #if 0 case 'S': charsys_dump_table(p ? p : "*"); @@ -921,11 +618,6 @@ int InitUnrealIRCd(int argc, char *argv[]) } # endif exit(0); -#endif -#ifndef _WIN32 - case 'k': - generate_cloakkeys(); - exit(0); #endif case 'K': { @@ -1032,7 +724,6 @@ int InitUnrealIRCd(int argc, char *argv[]) initstats(); if (!loop.config_test) DeleteTempModules(); - booted = FALSE; #if !defined(_WIN32) && !defined(_AMIGA) && !defined(OSXTIGER) && DEFAULT_PERMISSIONS != 0 /* Hack to stop people from being able to read the config file */ (void)chmod(CPATH, DEFAULT_PERMISSIONS); @@ -1056,11 +747,11 @@ int InitUnrealIRCd(int argc, char *argv[]) gettimeofday(&timeofday_tv, NULL); timeofday = timeofday_tv.tv_sec; url_socket_timeout(NULL); + unrealdns_timeout(NULL); fd_select(500); } if (config_test() < 0) exit(-1); - booted = TRUE; load_tunefile(); make_umodestr(); SetListening(&me); @@ -1223,6 +914,9 @@ void SocketLoop(void *dummy) reinit_tls(); doreloadcert = 0; } + /* If rehashing, check if we are done. */ + if (loop.rehashing && is_config_read_finished()) + rehash_internal(loop.rehash_save_client); } } diff --git a/src/ircd_vars.c b/src/ircd_vars.c new file mode 100644 index 0000000..6dec950 --- /dev/null +++ b/src/ircd_vars.c @@ -0,0 +1,31 @@ +/************************************************************************ + * UnrealIRCd - Unreal Internet Relay Chat Daemon - src/ircd_vars.c + * (c) 2021- Bram Matthys and The UnrealIRCd team + * License: GPLv2 + */ +#include "unrealircd.h" + +/** @file + * @brief UnrealIRCd global variables of the IRCd + */ + +int SVSNOOP = 0; +time_t timeofday = 0; +struct timeval timeofday_tv; +int tainted = 0; +LoopStruct loop; +MODVAR IRCCounts irccounts; +MODVAR Client me; /* That's me */ +MODVAR char *me_hash; +char *configfile = NULL; /* Server configuration file */ +int debuglevel = 0; /* Server debug level */ +int bootopt = 0; /* Server boot option flags */ +char *debugmode = ""; /* -"- -"- -"- */ +int dorehash = 0; /**< Rehash server on next socket loop */ +int dorestart = 0; /**< Restart server on next socket loop */ +int doreloadcert = 0; /**< Reload TLS certificate on next socket loop */ +#ifndef _WIN32 +char **myargv; +#else +LPCSTR cmdLine; +#endif diff --git a/src/list.c b/src/list.c index d752fb8..9435e75 100644 --- a/src/list.c +++ b/src/list.c @@ -44,6 +44,7 @@ MODVAR int numclients = 0; // TODO: Document whether servers are included or excluded in these lists... MODVAR struct list_head unknown_list; /**< Local clients in handshake (may become a user or server later) */ +MODVAR struct list_head control_list; /**< Local "control channel" clients */ MODVAR struct list_head lclient_list; /**< Local clients (users only, right?) */ MODVAR struct list_head client_list; /**< All clients - local and remote (not in handshake) */ MODVAR struct list_head server_list; /**< Locally connected servers */ @@ -71,6 +72,7 @@ void initlists(void) INIT_LIST_HEAD(&server_list); INIT_LIST_HEAD(&oper_list); INIT_LIST_HEAD(&unknown_list); + INIT_LIST_HEAD(&control_list); INIT_LIST_HEAD(&global_server_list); INIT_LIST_HEAD(&dead_list); diff --git a/src/log.c b/src/log.c index d21dabb..f2c674f 100644 --- a/src/log.c +++ b/src/log.c @@ -28,7 +28,7 @@ #include "unrealircd.h" // TODO: Make configurable at compile time (runtime won't do, as we haven't read the config file) -#define show_event_id_console 0 +#define show_event_console 0 /* Variables */ Log *logs[NUM_LOG_DESTINATIONS] = { NULL, NULL, NULL, NULL, NULL }; @@ -186,7 +186,7 @@ int config_test_log(ConfigFile *conf, ConfigEntry *block) /* TODO: Validate the sources lightly for formatting issues */ any_sources = 1; } - } + } else if (!strcmp(ce->name, "destination")) { for (cep = ce->items; cep; cep = cep->next) @@ -225,6 +225,22 @@ int config_test_log(ConfigFile *conf, ConfigEntry *block) cep->file->filename, cep->line_number, cep->value); errors++; } + for (cepp = cep->items; cepp; cepp = cepp->next) + { + if (!strcmp(cepp->name, "color")) + ; + else if (!strcmp(cepp->name, "show-event")) + ; + else if (!strcmp(cepp->name, "json-message-tag")) + ; + else if (!strcmp(cepp->name, "oper-only")) + ; + else + { + config_error_unknown(cepp->file->filename, cepp->line_number, "log::destination::channel", cepp->name); + errors++; + } + } } else if (!strcmp(cep->name, "file")) { @@ -308,6 +324,10 @@ int config_test_log(ConfigFile *conf, ConfigEntry *block) continue; } } + } else + { + config_error_unknown(ce->file->filename, ce->line_number, "log", ce->name); + errors++; } } @@ -393,6 +413,23 @@ int config_run_log(ConfigFile *conf, ConfigEntry *block) strlcpy(log->destination, cep->value, sizeof(log->destination)); /* destination is the channel */ log->sources = sources; AddListItem(log, temp_logs[LOG_DEST_CHANNEL]); + /* set defaults */ + log->color = tempiConf.server_notice_colors; + log->show_event = tempiConf.server_notice_show_event; + log->json_message_tag = 1; + log->oper_only = 1; + /* now parse options (if any) */ + for (cepp = cep->items; cepp; cepp = cepp->next) + { + if (!strcmp(cepp->name, "color")) + log->color = config_checkval(cepp->value, CFG_YESNO); + else if (!strcmp(cepp->name, "show-event")) + log->show_event = config_checkval(cepp->value, CFG_YESNO); + else if (!strcmp(cepp->name, "json-message-tag")) + log->json_message_tag = config_checkval(cepp->value, CFG_YESNO); + else if (!strcmp(cepp->name, "oper-only")) + log->oper_only = config_checkval(cepp->value, CFG_YESNO); + } } else if (!strcmp(cep->name, "remote")) { @@ -499,6 +536,10 @@ void json_expand_client(json_t *j, const char *key, Client *client, int detail) /* same for ip, is there for all (well, some services pseudo-users may not have one) */ json_object_set_new(child, "ip", json_string_unreal(client->ip)); + if (client->local && client->local->listener) + json_object_set_new(child, "server_port", json_integer(client->local->listener->port)); + if (client->local && client->local->port) + json_object_set_new(child, "client_port", json_integer(client->local->port)); /* client.details is always available: it is nick!user@host, nick@host, server@host * server@ip, or just server. @@ -963,7 +1004,6 @@ static NameValue log_colors_terminal[] = { { ULOG_ERROR, "\033[91m" }, { ULOG_FATAL, "\033[95m" }, }; -#define TERMINAL_COLOR_RESET "\033[0m" const char *log_level_irc_color(LogLevel loglevel) { @@ -1151,7 +1191,7 @@ literal: } /** Do the actual writing to log files */ -void do_unreal_log_disk(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized) +void do_unreal_log_disk(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized, Client *from_server) { static int last_log_file_warning = 0; Log *l; @@ -1171,14 +1211,14 @@ void do_unreal_log_disk(LogLevel loglevel, const char *subsystem, const char *ev for (m = msg; m; m = m->next) { #ifdef _WIN32 - if (show_event_id_console) + if (show_event_console) win_log("* %s.%s%s [%s] %s\n", subsystem, event_id, m->next?"+":"", log_level_valtostring(loglevel), m->line); else win_log("* [%s] %s\n", log_level_valtostring(loglevel), m->line); #else if (terminal_supports_color()) { - if (show_event_id_console) + if (show_event_console) { fprintf(stderr, "%s%s.%s%s %s[%s]%s %s\n", log_level_terminal_color(ULOG_INVALID), subsystem, event_id, TERMINAL_COLOR_RESET, @@ -1190,7 +1230,7 @@ void do_unreal_log_disk(LogLevel loglevel, const char *subsystem, const char *ev m->line); } } else { - if (show_event_id_console) + if (show_event_console) fprintf(stderr, "%s.%s%s [%s] %s\n", subsystem, event_id, m->next?"+":"", log_level_valtostring(loglevel), m->line); else fprintf(stderr, "[%s] %s\n", log_level_valtostring(loglevel), m->line); @@ -1303,13 +1343,9 @@ void do_unreal_log_disk(LogLevel loglevel, const char *subsystem, const char *ev for (m = msg; m; m = m->next) { char text_buf[8192]; - snprintf(text_buf, sizeof(text_buf), "%s.%s%s %s: %s\n", subsystem, event_id, m->next?"+":"", log_level_valtostring(loglevel), m->line); - // FIXME: don't write in 2 stages, waste of slow system calls - if (write(l->logfd, timebuf, strlen(timebuf)) < 0) - { - /* Let's ignore any write errors for this one. Next write() will catch it... */ - ; - } + snprintf(text_buf, sizeof(text_buf), "%s%s %s.%s%s %s: %s\n", + timebuf, from_server->name, + subsystem, event_id, m->next?"+":"", log_level_valtostring(loglevel), m->line); n = write(l->logfd, text_buf, strlen(text_buf)); if (n < strlen(text_buf)) { @@ -1423,13 +1459,66 @@ const char *log_to_snomask(LogLevel loglevel, const char *subsystem, const char #define COLOR_NONE "\xf" #define COLOR_DARKGREY "\00314" -/** Do the actual writing to log files */ + +/** Generic sendto function for logging to IRC. Used for notices to IRCOps and also for sending to individual users on channels */ +void sendto_log(Client *client, const char *msgtype, const char *destination, int show_colors, int show_event, + LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized, Client *from_server) +{ + MultiLine *m; + + for (m = msg; m; m = m->next) + { + MessageTag *mtags = NULL; + new_message(from_server, NULL, &mtags); + + /* Add JSON data, but only if it is the first message (m == msg) */ + if (json_serialized && (m == msg)) + { + MessageTag *json_mtag = safe_alloc(sizeof(MessageTag)); + safe_strdup(json_mtag->name, "unrealircd.org/json-log"); + safe_strdup(json_mtag->value, json_serialized); + AddListItem(json_mtag, mtags); + } + + if (show_colors) + { + if (show_event) + { + sendto_one(client, mtags, ":%s %s %s :%s%s.%s%s%s %s[%s]%s %s", + from_server->name, msgtype, destination, + COLOR_DARKGREY, subsystem, event_id, m->next?"+":"", COLOR_NONE, + log_level_irc_color(loglevel), log_level_valtostring(loglevel), COLOR_NONE, + m->line); + } else { + sendto_one(client, mtags, ":%s %s %s :%s[%s]%s %s", + from_server->name, msgtype, destination, + log_level_irc_color(loglevel), log_level_valtostring(loglevel), COLOR_NONE, + m->line); + } + } else { + if (show_event) + { + sendto_one(client, mtags, ":%s %s %s :%s.%s%s [%s] %s", + from_server->name, msgtype, destination, + subsystem, event_id, m->next?"+":"", + log_level_valtostring(loglevel), + m->line); + } else { + sendto_one(client, mtags, ":%s %s %s :[%s] %s", + from_server->name, msgtype, destination, + log_level_valtostring(loglevel), + m->line); + } + } + safe_free_message_tags(mtags); + } +} + +/** Send server notices to IRCOps */ void do_unreal_log_opers(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized, Client *from_server) { Client *client; const char *snomask_destinations, *p; - MessageTag *mtags = NULL, *mtags_loop; - MultiLine *m; /* If not fully booted then we don't have a logging to snomask mapping so can't do much.. */ if (!loop.booted) @@ -1443,20 +1532,13 @@ void do_unreal_log_opers(LogLevel loglevel, const char *subsystem, const char *e if (!snomask_destinations) return; - /* Prepare message tag for those who have CAP unrealircd.org/json-log */ - if (json_serialized) - { - mtags = safe_alloc(sizeof(MessageTag)); - safe_strdup(mtags->name, "unrealircd.org/json-log"); - safe_strdup(mtags->value, json_serialized); - } - /* To specific snomasks... */ list_for_each_entry(client, &oper_list, special_node) { const char *operlogin; ConfigItem_oper *oper; - int colors = iConf.server_notice_colors; + int show_colors = iConf.server_notice_colors; + int show_event = iConf.server_notice_show_event; if (snomask_destinations) { @@ -1477,30 +1559,56 @@ void do_unreal_log_opers(LogLevel loglevel, const char *subsystem, const char *e operlogin = get_operlogin(client); if (operlogin && (oper = find_oper(operlogin))) - colors = oper->server_notice_colors; - - mtags_loop = mtags; - for (m = msg; m; m = m->next) { - if (colors) - { - sendto_one(client, mtags_loop, ":%s NOTICE %s :%s%s.%s%s%s %s[%s]%s %s", - from_server->name, client->name, - COLOR_DARKGREY, subsystem, event_id, m->next?"+":"", COLOR_NONE, - log_level_irc_color(loglevel), log_level_valtostring(loglevel), COLOR_NONE, - m->line); - } else { - sendto_one(client, mtags_loop, ":%s NOTICE %s :%s.%s%s [%s] %s", - from_server->name, client->name, - subsystem, event_id, m->next?"+":"", - log_level_valtostring(loglevel), - m->line); - } - mtags_loop = NULL; /* this way we only send the JSON in the first msg */ + show_colors = oper->server_notice_colors; + show_event = oper->server_notice_show_event; + } + + sendto_log(client, "NOTICE", client->name, show_colors, show_event, loglevel, subsystem, event_id, msg, json_serialized, from_server); + } +} + +/** Send server notices to channels */ +void do_unreal_log_channels(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized, Client *from_server) +{ + Log *l; + Member *m; + Client *client; + + /* If not fully booted then we don't have a logging to snomask mapping so can't do much.. */ + if (!loop.booted) + return; + + /* Never send these */ + if (!strcmp(subsystem, "rawtraffic")) + return; + + for (l = logs[LOG_DEST_CHANNEL]; l; l = l->next) + { + const char *operlogin; + ConfigItem_oper *oper; + Channel *channel; + + if (!log_sources_match(l->sources, loglevel, subsystem, event_id, 0)) + continue; + + channel = find_channel(l->destination); + if (!channel) + continue; + + for (m = channel->members; m; m = m->next) + { + Client *client = m->client; + if (!MyUser(client)) + continue; + if (l->oper_only && !IsOper(client)) + continue; + sendto_log(client, "PRIVMSG", channel->name, l->color, l->show_event, + loglevel, subsystem, event_id, msg, + l->json_message_tag ? json_serialized : NULL, + from_server); } } - - safe_free_message_tags(mtags); } void do_unreal_log_remote(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized) @@ -1522,6 +1630,25 @@ void do_unreal_log_remote(LogLevel loglevel, const char *subsystem, const char * do_unreal_log_remote_deliver(loglevel, subsystem, event_id, msg, json_serialized); } +/** Send server notices to control channel */ +void do_unreal_log_control(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized, Client *from_server) +{ + Client *client; + MultiLine *m; + + if (!loop.booted) + return; + + /* Never send these */ + if (!strcmp(subsystem, "rawtraffic")) + return; + + list_for_each_entry(client, &control_list, lclient_node) + if (IsMonitorRehash(client)) + for (m = msg; m; m = m->next) + sendto_one(client, NULL, "REPLY [%s] %s", log_level_valtostring(loglevel), m->line); +} + void do_unreal_log_free_args(va_list vl) { LogData *d; @@ -1693,17 +1820,24 @@ void do_unreal_log_internal(LogLevel loglevel, const char *subsystem, const char /* Convert the message buffer to MultiLine */ mmsg = line2multiline(msgbuf); - /* Now call the disk loggers */ - do_unreal_log_disk(loglevel, subsystem, event_id, mmsg, json_serialized); - - /* And the ircops stuff */ + /* Parse the "from server" info, if any */ t = json_object_get(j_details, "from_server_name"); if (t && (str = json_get_value(t))) from_server = find_server(str, NULL); if (from_server == NULL) from_server = &me; + + /* Now call all the loggers: */ + + do_unreal_log_disk(loglevel, subsystem, event_id, mmsg, json_serialized, from_server); + + if ((loop.rehashing == 2) || !strcmp(subsystem, "config")) + do_unreal_log_control(loglevel, subsystem, event_id, mmsg, json_serialized, from_server); + do_unreal_log_opers(loglevel, subsystem, event_id, mmsg, json_serialized, from_server); + do_unreal_log_channels(loglevel, subsystem, event_id, mmsg, json_serialized, from_server); + do_unreal_log_remote(loglevel, subsystem, event_id, mmsg, json_serialized); // NOTE: code duplication further down! @@ -1723,10 +1857,11 @@ void do_unreal_log_internal_from_remote(LogLevel loglevel, const char *subsystem unreal_log_recursion_trap = 1; /* Call the disk loggers */ - do_unreal_log_disk(loglevel, subsystem, event_id, msg, json_serialized); + do_unreal_log_disk(loglevel, subsystem, event_id, msg, json_serialized, from_server); - /* And the ircops stuff */ + /* And to IRC */ do_unreal_log_opers(loglevel, subsystem, event_id, msg, json_serialized, from_server); + do_unreal_log_channels(loglevel, subsystem, event_id, msg, json_serialized, from_server); unreal_log_recursion_trap = 0; } diff --git a/src/misc.c b/src/misc.c index 27c4f36..2d381ef 100644 --- a/src/misc.c +++ b/src/misc.c @@ -623,8 +623,8 @@ void exit_client_ex(Client *client, Client *origin, MessageTag *recv_mtags, cons if (client->local->fd >= 0 && !IsConnecting(client)) { - sendto_one(client, NULL, "ERROR :Closing Link: %s (%s)", - get_client_name(client, FALSE), comment); + if (!IsControl(client)) + sendto_one(client, NULL, "ERROR :Closing Link: %s (%s)", get_client_name(client, FALSE), comment); } close_connection(client); } @@ -2369,3 +2369,173 @@ void addlettertodynamicstringsorted(char **str, char letter) safe_free_raw(*str); *str = newbuf; } + +void s_die() +{ +#ifdef _WIN32 + Client *client; + if (!IsService) + { + loop.terminating = 1; + unload_all_modules(); + + list_for_each_entry(client, &lclient_list, lclient_node) + (void) send_queued(client); + + exit(-1); + } + else { + SERVICE_STATUS status; + SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + SC_HANDLE hService = OpenService(hSCManager, "UnrealIRCd", SERVICE_STOP); + ControlService(hService, SERVICE_CONTROL_STOP, &status); + } +#else + loop.terminating = 1; + unload_all_modules(); + unlink(conf_files ? conf_files->pid_file : IRCD_PIDFILE); + exit(0); +#endif +} + +#ifndef _WIN32 +void s_rehash() +{ + struct sigaction act; + dorehash = 1; + act.sa_handler = s_rehash; + act.sa_flags = 0; + (void)sigemptyset(&act.sa_mask); + (void)sigaddset(&act.sa_mask, SIGHUP); + (void)sigaction(SIGHUP, &act, NULL); +} + +void s_reloadcert() +{ + struct sigaction act; + doreloadcert = 1; + act.sa_handler = s_reloadcert; + act.sa_flags = 0; + (void)sigemptyset(&act.sa_mask); + (void)sigaddset(&act.sa_mask, SIGUSR1); + (void)sigaction(SIGUSR1, &act, NULL); +} +#endif // #ifndef _WIN32 + +void restart(const char *mesg) +{ + server_reboot(mesg); +} + +void s_restart() +{ + dorestart = 1; +} + +#ifndef _WIN32 +/** Signal handler for signals which we ignore, + * like SIGPIPE ("Broken pipe") and SIGWINCH (terminal window changed) etc. + */ +void ignore_this_signal() +{ + struct sigaction act; + + act.sa_handler = ignore_this_signal; + act.sa_flags = 0; + (void)sigemptyset(&act.sa_mask); + (void)sigaddset(&act.sa_mask, SIGALRM); + (void)sigaddset(&act.sa_mask, SIGPIPE); + (void)sigaction(SIGALRM, &act, (struct sigaction *)NULL); + (void)sigaction(SIGPIPE, &act, (struct sigaction *)NULL); +#ifdef SIGWINCH + (void)sigaddset(&act.sa_mask, SIGWINCH); + (void)sigaction(SIGWINCH, &act, (struct sigaction *)NULL); +#endif +} +#endif /* #ifndef _WIN32 */ + + +void server_reboot(const char *mesg) +{ + int i; + Client *client; + unreal_log(ULOG_INFO, "main", "UNREALIRCD_RESTARTING", NULL, + "Restarting server: $reason", + log_data_string("reason", mesg)); + + list_for_each_entry(client, &lclient_list, lclient_node) + (void) send_queued(client); + + /* + * ** fd 0 must be 'preserved' if either the -d or -i options have + * ** been passed to us before restarting. + */ +#ifdef HAVE_SYSLOG + (void)closelog(); +#endif +#ifndef _WIN32 + for (i = 3; i < MAXCONNECTIONS; i++) + (void)close(i); + if (!(bootopt & (BOOT_TTY | BOOT_DEBUG))) + (void)close(2); + (void)close(1); + (void)close(0); + close_std_descriptors(); + (void)execv(MYNAME, myargv); +#else + close_connections(); + if (!IsService) + { + CleanUp(); + WinExec(cmdLine, SW_SHOWDEFAULT); + } +#endif + unload_all_modules(); +#ifdef _WIN32 + if (IsService) + { + SERVICE_STATUS status; + PROCESS_INFORMATION pi; + STARTUPINFO si; + char fname[MAX_PATH]; + memset(&status, 0, sizeof(status)); + memset(&si, 0, sizeof(si)); + IRCDStatus.dwCurrentState = SERVICE_STOP_PENDING; + SetServiceStatus(IRCDStatusHandle, &IRCDStatus); + GetModuleFileName(GetModuleHandle(NULL), fname, MAX_PATH); + CreateProcess(fname, "restartsvc", NULL, NULL, FALSE, + 0, NULL, NULL, &si, &pi); + IRCDStatus.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(IRCDStatusHandle, &IRCDStatus); + ExitProcess(0); + } + else +#endif + exit(-1); +} + +/** Check if at least 'minimum' seconds passed by since last run. + * @param tv_old Pointer to a timeval struct to keep track of things. + * @param minimum The time specified in milliseconds (eg: 1000 for 1 second) + * @returns When 'minimum' msec passed 1 is returned and the time is reset, otherwise 0 is returned. + */ +int minimum_msec_since_last_run(struct timeval *tv_old, long minimum) +{ + long v; + + if (tv_old->tv_sec == 0) + { + /* First call ever */ + tv_old->tv_sec = timeofday_tv.tv_sec; + tv_old->tv_usec = timeofday_tv.tv_usec; + return 0; + } + v = ((timeofday_tv.tv_sec - tv_old->tv_sec) * 1000) + ((timeofday_tv.tv_usec - tv_old->tv_usec)/1000); + if (v >= minimum) + { + tv_old->tv_sec = timeofday_tv.tv_sec; + tv_old->tv_usec = timeofday_tv.tv_usec; + return 1; + } + return 0; +} diff --git a/src/modules/blacklist.c b/src/modules/blacklist.c index 696b8fa..3cbdec9 100644 --- a/src/modules/blacklist.c +++ b/src/modules/blacklist.c @@ -99,6 +99,7 @@ void blacklist_free_conf(void); void delete_blacklist_block(Blacklist *e); void blacklist_md_free(ModData *md); int blacklist_handshake(Client *client); +int blacklist_ip_change(Client *client, const char *oldip); int blacklist_quit(Client *client, MessageTag *mtags, const char *comment); int blacklist_preconnect(Client *client); void blacklist_resolver_callback(void *arg, int status, int timeouts, struct hostent *he); @@ -146,6 +147,7 @@ MOD_INIT() HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, blacklist_config_run); HookAdd(modinfo->handle, HOOKTYPE_HANDSHAKE, 0, blacklist_handshake); + HookAdd(modinfo->handle, HOOKTYPE_IP_CHANGE, 0, blacklist_ip_change); HookAdd(modinfo->handle, HOOKTYPE_PRE_LOCAL_CONNECT, 0, blacklist_preconnect); HookAdd(modinfo->handle, HOOKTYPE_REHASH, 0, blacklist_rehash); HookAdd(modinfo->handle, HOOKTYPE_REHASH_COMPLETE, 0, blacklist_rehash_complete); @@ -553,6 +555,12 @@ int blacklist_handshake(Client *client) return 0; } +int blacklist_ip_change(Client *client, const char *oldip) +{ + blacklist_start_check(client); + return 0; +} + int blacklist_start_check(Client *client) { Blacklist *bl; diff --git a/src/modules/chanmodes/halfop.c b/src/modules/chanmodes/halfop.c index 2e2a6ba..a7e6b6f 100644 --- a/src/modules/chanmodes/halfop.c +++ b/src/modules/chanmodes/halfop.c @@ -73,9 +73,9 @@ int cmode_halfop_is_ok(Client *client, Channel *channel, char mode, const char * /* User may always remove their own modes */ return EX_ALLOW; } - if ((what == MODE_ADD) && check_channel_access(client, channel, "hoaq")) + if (check_channel_access(client, channel, "oaq")) { - /* Permitted for +hoaq */ + /* Permitted for +oaq */ return EX_ALLOW; } if (type == EXCHK_ACCESS_ERR) diff --git a/src/modules/channeldb.c b/src/modules/channeldb.c index 9c4f0c2..6330a5a 100644 --- a/src/modules/channeldb.c +++ b/src/modules/channeldb.c @@ -384,10 +384,24 @@ int read_listmode(UnrealDB *db, Ban **lst) for (i = 0; i < total; i++) { + const char *str; e = safe_alloc(sizeof(Ban)); R_SAFE(unrealdb_read_str(db, &e->banstr)); R_SAFE(unrealdb_read_str(db, &e->who)); R_SAFE(unrealdb_read_int64(db, &when)); + str = clean_ban_mask(e->banstr, MODE_ADD, &me, 0); + if (str == NULL) + { + /* Skip this item */ + config_warn("[channeldb] listmode skipped (no longer valid?): %s", e->banstr); + safe_free(e->banstr); + safe_free(e->who); + safe_free(e); + continue; + } + safe_strdup(e->banstr, str); + + /* Add to list */ e->when = when; e->next = *lst; *lst = e; diff --git a/src/modules/chghost.c b/src/modules/chghost.c index a52c883..814394b 100644 --- a/src/modules/chghost.c +++ b/src/modules/chghost.c @@ -219,7 +219,7 @@ void _userhost_changed(Client *client) } } - RunHook(HOOKTYPE_USERHOST_CHANGED, client, remember_user, remember_host); + RunHook(HOOKTYPE_USERHOST_CHANGE, client, remember_user, remember_host); if (MyUser(client)) { diff --git a/src/modules/dccdeny.c b/src/modules/dccdeny.c index 0fda03f..4fe3a2e 100644 --- a/src/modules/dccdeny.c +++ b/src/modules/dccdeny.c @@ -25,7 +25,7 @@ ModuleHeader MOD_HEADER = { "dccdeny", - "5.0", + "6.0.2", "command /dccdeny", "UnrealIRCd Team", "unrealircd-6", @@ -525,11 +525,8 @@ int dccdeny_can_send_to_channel(Client *client, Channel *channel, Membership *lp const char *filename = get_dcc_filename(*msg); if (filename && !can_dcc(client, channel->name, NULL, filename, &err)) { - if (!IsDead(client) && (sendtype != SEND_TYPE_NOTICE)) - { - strlcpy(errbuf, err, sizeof(errbuf)); - *errmsg = errbuf; - } + strlcpy(errbuf, err, sizeof(errbuf)); + *errmsg = errbuf; return HOOK_DENY; } } @@ -649,7 +646,11 @@ static int can_dcc(Client *client, const char *target, Client *targetcli, const } if (match_spamfilter(client, filename, SPAMF_DCC, "PRIVMSG", target, 0, NULL)) + { + /* Dirty hack, yeah spamfilter already sent the error message :( */ + *errmsg = ""; return 0; + } if ((fl = dcc_isforbidden(client, filename))) { diff --git a/src/modules/extbans/realname.c b/src/modules/extbans/realname.c index 074948e..f2e94c9 100644 --- a/src/modules/extbans/realname.c +++ b/src/modules/extbans/realname.c @@ -43,7 +43,7 @@ MOD_INIT() req.conv_param = extban_realname_conv_param; req.is_banned = extban_realname_is_banned; req.is_banned_events = BANCHK_ALL|BANCHK_TKL; - req.options = EXTBOPT_CHSVSMODE|EXTBOPT_INVEX|EXTBOPT_TKL; + req.options = EXTBOPT_INVEX|EXTBOPT_TKL; if (!ExtbanAdd(modinfo->handle, req)) { config_error("could not register extended ban type"); diff --git a/src/modules/extbans/timedban.c b/src/modules/extbans/timedban.c index 5ec418c..af20c52 100644 --- a/src/modules/extbans/timedban.c +++ b/src/modules/extbans/timedban.c @@ -78,7 +78,6 @@ MOD_INIT() extban.letter = 't'; extban.name = "time"; extban.options |= EXTBOPT_ACTMODIFIER; /* not really, but ours shouldn't be stacked from group 1 */ - extban.options |= EXTBOPT_CHSVSMODE; /* so "SVSMODE -nick" will unset affected ~t extbans */ extban.options |= EXTBOPT_INVEX; /* also permit timed invite-only exceptions (+I) */ extban.conv_param = timedban_extban_conv_param; extban.is_ok = timedban_extban_is_ok; diff --git a/src/modules/extended-monitor.c b/src/modules/extended-monitor.c index 34c9f1f..b4aed6f 100644 --- a/src/modules/extended-monitor.c +++ b/src/modules/extended-monitor.c @@ -26,8 +26,8 @@ long CAP_EXTENDED_MONITOR = 0L; int extended_monitor_away(Client *client, MessageTag *mtags, const char *reason, int already_as_away); int extended_monitor_account_login(Client *client, MessageTag *mtags); -int extended_monitor_userhost_changed(Client *client, const char *olduser, const char *oldhost); -int extended_monitor_realname_changed(Client *client, const char *oldinfo); +int extended_monitor_userhost_change(Client *client, const char *olduser, const char *oldhost); +int extended_monitor_realname_change(Client *client, const char *oldinfo); int extended_monitor_notification(Client *client, Watch *watch, Link *lp, int event); ModuleHeader MOD_HEADER @@ -59,8 +59,8 @@ MOD_INIT() HookAdd(modinfo->handle, HOOKTYPE_AWAY, 0, extended_monitor_away); HookAdd(modinfo->handle, HOOKTYPE_ACCOUNT_LOGIN, 0, extended_monitor_account_login); - HookAdd(modinfo->handle, HOOKTYPE_USERHOST_CHANGED, 0, extended_monitor_userhost_changed); - HookAdd(modinfo->handle, HOOKTYPE_REALNAME_CHANGED, 0, extended_monitor_realname_changed); + HookAdd(modinfo->handle, HOOKTYPE_USERHOST_CHANGE, 0, extended_monitor_userhost_change); + HookAdd(modinfo->handle, HOOKTYPE_REALNAME_CHANGE, 0, extended_monitor_realname_change); return MOD_SUCCESS; } @@ -95,13 +95,13 @@ int extended_monitor_account_login(Client *client, MessageTag *mtags) return 0; } -int extended_monitor_userhost_changed(Client *client, const char *olduser, const char *oldhost) +int extended_monitor_userhost_change(Client *client, const char *olduser, const char *oldhost) { watch_check(client, WATCH_EVENT_USERHOST, extended_monitor_notification); return 0; } -int extended_monitor_realname_changed(Client *client, const char *oldinfo) +int extended_monitor_realname_change(Client *client, const char *oldinfo) { watch_check(client, WATCH_EVENT_REALNAME, extended_monitor_notification); return 0; diff --git a/src/modules/geoip_base.c b/src/modules/geoip_base.c index 3244e1f..1cf2ea1 100644 --- a/src/modules/geoip_base.c +++ b/src/modules/geoip_base.c @@ -25,6 +25,7 @@ void geoip_base_free(ModData *m); const char *geoip_base_serialize(ModData *m); void geoip_base_unserialize(const char *str, ModData *m); int geoip_base_handshake(Client *client); +int geoip_base_ip_change(Client *client, const char *oldip); int geoip_base_whois(Client *client, Client *target, NameValuePrioList **list); int geoip_connect_extinfo(Client *client, NameValuePrioList **list); int geoip_base_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); @@ -119,10 +120,10 @@ MOD_INIT() HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, geoip_base_configrun); HookAdd(modinfo->handle, HOOKTYPE_HANDSHAKE, 0, geoip_base_handshake); + HookAdd(modinfo->handle, HOOKTYPE_IP_CHANGE, 0, geoip_base_ip_change); HookAdd(modinfo->handle, HOOKTYPE_SERVER_HANDSHAKE_OUT, 0, geoip_base_handshake); HookAdd(modinfo->handle, HOOKTYPE_CONNECT_EXTINFO, 1, geoip_connect_extinfo); /* (prio: near-first) */ HookAdd(modinfo->handle, HOOKTYPE_PRE_LOCAL_CONNECT, 0,geoip_base_handshake); /* in case the IP changed in registration phase (WEBIRC, HTTP Forwarded) */ - HookAdd(modinfo->handle, HOOKTYPE_REMOTE_CONNECT, 0, geoip_base_handshake); /* remote user */ HookAdd(modinfo->handle, HOOKTYPE_WHOIS, 0, geoip_base_whois); CommandAdd(modinfo->handle, "GEOIP", cmd_geoip, MAXPARA, CMD_USER); @@ -167,6 +168,12 @@ int geoip_base_handshake(Client *client) return 0; } +int geoip_base_ip_change(Client *client, const char *oldip) +{ + geoip_base_handshake(client); + return 0; +} + void geoip_base_free(ModData *m) { if (m->ptr) @@ -229,12 +236,13 @@ void geoip_base_unserialize(const char *str, ModData *m) m->ptr = res; } -EVENT(geoip_base_set_existing_users_evt){ +EVENT(geoip_base_set_existing_users_evt) +{ Client *client; - list_for_each_entry(client, &client_list, client_node){ - if (!IsUser(client)) - continue; - geoip_base_handshake(client); + list_for_each_entry(client, &client_list, client_node) + { + if (MyUser(client)) + geoip_base_handshake(client); } } diff --git a/src/modules/hideserver.c b/src/modules/hideserver.c index 4e22bca..4ed3ab9 100644 --- a/src/modules/hideserver.c +++ b/src/modules/hideserver.c @@ -43,6 +43,22 @@ static ModuleInfo *MyModInfo; #define MyMod MyModInfo->handle #define SAVE_MODINFO MyModInfo = modinfo; +static int lmax = 0; +static int umax = 0; + +static int dcount(int n) +{ + int cnt = 0; + + while (n != 0) + { + n = n/10; + cnt++; + } + + return cnt; +} + ModuleHeader MOD_HEADER = { "hideserver", @@ -230,8 +246,24 @@ static void dump_map(Client *client, Client *server, char *mask, int prompt_leng sendnumeric(client, RPL_MAPMORE, prompt, length, server->name); else { - sendnumeric(client, RPL_MAP, prompt, - length, server->name, server->server->users, IsOper(client) ? server->id : ""); + char tbuf[256]; + char sid[10]; + int len = length - strlen(server->name) + 1; + + if (len < 0) + len = 0; + if (len > 255) + len = 255; + + tbuf[len--] = '\0'; + while (len >= 0) + tbuf[len--] = '-'; + if (IsOper(client)) + snprintf(sid, sizeof(sid), " [%s]", server->id); + sendnumeric(client, RPL_MAP, prompt, server->name, tbuf, umax, + server->server->users, (double)(lmax < 10) ? 4 : (lmax == 100) ? 6 : 5, + (server->server->users * 100.0 / irccounts.clients), + IsOper(client) ? sid : ""); cnt = 0; } @@ -279,12 +311,25 @@ static void dump_map(Client *client, Client *server, char *mask, int prompt_leng void dump_flat_map(Client *client, Client *server, int length) { char buf[4]; + char tbuf[256]; Client *acptr; - int cnt = 0, hide_ulines; + int cnt = 0, len = 0, hide_ulines; hide_ulines = (HIDE_ULINES && !ValidatePermissionsForPath("server:info:map:ulines",client,NULL,NULL,NULL)) ? 1 : 0; - sendnumeric(client, RPL_MAP, "", length, server->name, server->server->users, ""); + len = length - strlen(server->name) + 3; + if (len < 0) + len = 0; + if (len > 255) + len = 255; + + tbuf[len--] = '\0'; + while (len >= 0) + tbuf[len--] = '-'; + + sendnumeric(client, RPL_MAP, "", server->name, tbuf, umax, server->server->users, + (lmax < 10) ? 4 : (lmax == 100) ? 6 : 5, + (server->server->users * 100.0 / irccounts.clients), ""); list_for_each_entry(acptr, &global_server_list, client_node) { @@ -304,7 +349,20 @@ void dump_flat_map(Client *client, Client *server, int length) break; if (--cnt == 0) *buf = '`'; - sendnumeric(client, RPL_MAP, buf, length-2, acptr->name, acptr->server->users, ""); + + len = length - strlen(acptr->name) + 1; + if (len < 0) + len = 0; + if (len > 255) + len = 255; + + tbuf[len--] = '\0'; + while (len >= 0) + tbuf[len--] = '-'; + + sendnumeric(client, RPL_MAP, buf, acptr->name, tbuf, umax, acptr->server->users, + (lmax < 10) ? 4 : (lmax == 100) ? 6 : 5, + (acptr->server->users * 100.0 / irccounts.clients), ""); } } @@ -318,6 +376,10 @@ CMD_OVERRIDE_FUNC(override_map) { Client *acptr; int longest = strlen(me.name); + float avg_users = 0.0; + + umax = 0; + lmax = 0; if (parc < 2) parv[1] = "*"; @@ -339,10 +401,16 @@ CMD_OVERRIDE_FUNC(override_map) list_for_each_entry(acptr, &global_server_list, client_node) { + int perc = 0; if (FindHiddenServer(acptr->name)) break; + perc = (acptr->server->users * 100 / irccounts.clients); if ((strlen(acptr->name) + acptr->hopcount * 2) > longest) longest = strlen(acptr->name) + acptr->hopcount * 2; + if (lmax < perc) + lmax = perc; + if (umax < dcount(acptr->server->users)) + umax = dcount(acptr->server->users); } if (longest > 60) @@ -354,6 +422,9 @@ CMD_OVERRIDE_FUNC(override_map) else dump_map(client, &me, "*", 0, longest); + avg_users = irccounts.clients * 1.0 / irccounts.servers; + sendnumeric(client, RPL_MAPUSERS, irccounts.servers, (irccounts.servers > 1 ? "s" : ""), irccounts.clients, + (irccounts.clients > 1 ? "s" : ""), avg_users); sendnumeric(client, RPL_MAPEND); } diff --git a/src/modules/labeled-response.c b/src/modules/labeled-response.c index 5027fe0..d1a0f4f 100644 --- a/src/modules/labeled-response.c +++ b/src/modules/labeled-response.c @@ -196,7 +196,7 @@ int lr_post_command(Client *from, MessageTag *mtags, const char *buf) */ int more_tags = currentcmd.firstbuf[0] == '@'; currentcmd.client = NULL; /* prevent lr_packet from interfering */ - snprintf(packet, sizeof(packet), + snprintf(packet, sizeof(packet)-3, "@label=%s%s%s\r\n", currentcmd.label, more_tags ? ";" : " ", diff --git a/src/modules/list.c b/src/modules/list.c index f7ece82..c0515c5 100644 --- a/src/modules/list.c +++ b/src/modules/list.c @@ -31,7 +31,7 @@ ModuleHeader MOD_HEADER = { "list", "5.0", - "command /list", + "command /LIST", "UnrealIRCd Team", "unrealircd-6", }; @@ -136,8 +136,8 @@ CMD_FUNC(cmd_list) "use, and what channels LIST will return when you use them.", ">number List channels with more than people.", " people.", - "C>number List channels created between now and minutes ago.", - "C minutes ago.", + "C>number List channels created more than minutes ago.", + "C minutes ago.", "T>number List channels whose topics are older than minutes", " (Ie, they have not changed in the last minutes.", "T minutes.", @@ -199,56 +199,52 @@ CMD_FUNC(cmd_list) } switch (*name) { - case '<': - usermax = atoi(name + 1) - 1; - doall = 1; - break; - case '>': - usermin = atoi(name + 1) + 1; - doall = 1; - break; - case 'C': - case 'c': /* Channel time -- creation time? */ - ++name; - switch (*name++) - { - case '<': - chantimemax = currenttime - 60 * atoi(name); - doall = 1; - break; - case '>': - chantimemin = currenttime - 60 * atoi(name); - doall = 1; - break; - default: - sendnumeric(client, ERR_LISTSYNTAX); - error = 1; - } - break; -#ifdef LIST_USE_T - case 'T': - case 't': - ++name; - switch (*name++) - { - case '<': - topictimemax = - currenttime - 60 * atoi(name); - doall = 1; - break; - case '>': - topictimemin = - currenttime - 60 * atoi(name); - doall = 1; - break; - default: - sendnumeric(client, ERR_LISTSYNTAX, - "Bad list syntax, type /list ?"); - error = 1; - } - break; -#endif - default: /* A channel, possibly with wildcards. + case '<': + usermax = atoi(name + 1) - 1; + doall = 1; + break; + case '>': + usermin = atoi(name + 1) + 1; + doall = 1; + break; + case 'C': + case 'c': /* Channel time -- creation time? */ + ++name; + switch (*name++) + { + case '<': + chantimemin = currenttime - 60 * atoi(name); + doall = 1; + break; + case '>': + chantimemax = currenttime - 60 * atoi(name); + doall = 1; + break; + default: + sendnumeric(client, ERR_LISTSYNTAX); + error = 1; + } + break; + case 'T': + case 't': + ++name; + switch (*name++) + { + case '<': + topictimemin = currenttime - 60 * atoi(name); + doall = 1; + break; + case '>': + topictimemax = currenttime - 60 * atoi(name); + doall = 1; + break; + default: + sendnumeric(client, ERR_LISTSYNTAX); + error = 1; + } + break; + default: + /* A channel, possibly with wildcards. * Thought for the future: Consider turning wildcard * processing on the fly. * new syntax: !channelmask will tell ircd to ignore @@ -259,35 +255,38 @@ CMD_FUNC(cmd_list) * channel even if any of the !channelmask masks * matches it. */ - if (*name == '!') - { - doall = 1; - add_name_list(nolist, name + 1); - } - else if (strchr(name, '*') || strchr(name, '?')) - { - doall = 1; - add_name_list(yeslist, name); - } - else /* Just a normal channel */ - { - channel = find_channel(name); - if (channel && (ShowChannel(client, channel) || ValidatePermissionsForPath("channel:see:list:secret",client,NULL,channel,NULL))) { - modebuf[0] = '['; - channel_modes(client, modebuf+1, parabuf, sizeof(modebuf)-1, sizeof(parabuf), channel, 0); - if (modebuf[2] == '\0') - modebuf[0] = '\0'; - else - strlcat(modebuf, "]", sizeof modebuf); - sendnumeric(client, RPL_LIST, - name, channel->users, - modebuf, - (channel->topic ? channel->topic : - "")); -} - } - } /* switch */ - } /* while */ + if (*name == '!') + { + /* Negative matching by name */ + doall = 1; + add_name_list(nolist, name + 1); + } + else if (strchr(name, '*') || strchr(name, '?')) + { + /* Channel with wildcards */ + doall = 1; + add_name_list(yeslist, name); + } + else + { + /* A specific channel name without wildcards */ + channel = find_channel(name); + if (channel && (ShowChannel(client, channel) || ValidatePermissionsForPath("channel:see:list:secret",client,NULL,channel,NULL))) + { + modebuf[0] = '['; + channel_modes(client, modebuf+1, parabuf, sizeof(modebuf)-1, sizeof(parabuf), channel, 0); + + if (modebuf[2] == '\0') + modebuf[0] = '\0'; + else + strlcat(modebuf, "]", sizeof modebuf); + + sendnumeric(client, RPL_LIST, name, channel->users, modebuf, + channel->topic ? channel->topic : ""); + } + } + } /* switch */ + } /* for */ if (doall) { @@ -335,7 +334,7 @@ int send_list(Client *client) * choice of numsend. -Rak */ - /* Begin of /list? then send official channels. */ + /* Begin of /LIST? then send official channels first. */ if ((lopt->starthash == 0) && conf_offchans) { ConfigItem_offchans *x; @@ -343,18 +342,15 @@ int send_list(Client *client) { if (find_channel(x->name)) continue; /* exists, >0 users.. will be sent later */ - sendnumeric(client, RPL_LIST, x->name, - 0, - "", - x->topic ? x->topic : ""); + sendnumeric(client, RPL_LIST, x->name, 0, "", + x->topic ? x->topic : ""); } } for (hashnum = lopt->starthash; hashnum < CHAN_HASH_TABLE_SIZE; hashnum++) { if (numsend > 0) - for (channel = hash_get_chan_bucket(hashnum); - channel; channel = channel->hnextch) + for (channel = hash_get_chan_bucket(hashnum); channel; channel = channel->hnextch) { if (SecretChannel(channel) && !IsMember(client, channel) @@ -373,15 +369,13 @@ int send_list(Client *client) if ((!lopt->showall)) { /* User count must be in range */ - if ((channel->users < lopt->usermin) || - ((lopt->usermax >= 0) && (channel->users > - lopt->usermax))) + if ((channel->users < lopt->usermin) || + ((lopt->usermax >= 0) && (channel->users > lopt->usermax))) continue; /* Creation time must be in range */ - if ((channel->creationtime && (channel->creationtime < - lopt->chantimemin)) || (channel->creationtime > - lopt->chantimemax)) + if ((channel->creationtime && (channel->creationtime < lopt->chantimemin)) || + (channel->creationtime > lopt->chantimemax)) continue; /* Topic time must be in range */ @@ -432,7 +426,7 @@ int send_list(Client *client) return 0; } - /* + /* * We've exceeded the limit on the number of channels to send back * at once. */ diff --git a/src/modules/map.c b/src/modules/map.c index 1f57e9e..f743941 100644 --- a/src/modules/map.c +++ b/src/modules/map.c @@ -26,6 +26,22 @@ CMD_FUNC(cmd_map); #define MSG_MAP "MAP" +static int lmax = 0; +static int umax = 0; + +static int dcount(int n) +{ + int cnt = 0; + + while (n != 0) + { + n = n/10; + cnt++; + } + + return cnt; +} + ModuleHeader MOD_HEADER = { "map", @@ -70,8 +86,24 @@ static void dump_map(Client *client, Client *server, char *mask, int prompt_leng sendnumeric(client, RPL_MAPMORE, prompt, length, server->name); else { - sendnumeric(client, RPL_MAP, prompt, - length, server->name, server->server->users, IsOper(client) ? server->id : ""); + char tbuf[256]; + char sid[10]; + int len = length - strlen(server->name) + 1; + + if (len < 0) + len = 0; + if (len > 255) + len = 255; + + tbuf[len--] = '\0'; + while (len >= 0) + tbuf[len--] = '-'; + if (IsOper(client)) + snprintf(sid, sizeof(sid), " [%s]", server->id); + sendnumeric(client, RPL_MAP, prompt, server->name, tbuf, umax, + server->server->users, (double)(lmax < 10) ? 4 : (lmax == 100) ? 6 : 5, + (server->server->users * 100.0 / irccounts.clients), + IsOper(client) ? sid : ""); cnt = 0; } @@ -115,12 +147,25 @@ static void dump_map(Client *client, Client *server, char *mask, int prompt_leng void dump_flat_map(Client *client, Client *server, int length) { char buf[4]; + char tbuf[256]; Client *acptr; - int cnt = 0, hide_ulines; + int cnt = 0, len = 0, hide_ulines; hide_ulines = (HIDE_ULINES && !ValidatePermissionsForPath("server:info:map:ulines",client,NULL,NULL,NULL)) ? 1 : 0; - sendnumeric(client, RPL_MAP, "", length, server->name, server->server->users, ""); + len = length - strlen(server->name) + 3; + if (len < 0) + len = 0; + if (len > 255) + len = 255; + + tbuf[len--] = '\0'; + while (len >= 0) + tbuf[len--] = '-'; + + sendnumeric(client, RPL_MAP, "", server->name, tbuf, umax, server->server->users, + (lmax < 10) ? 4 : (lmax == 100) ? 6 : 5, + (server->server->users * 100.0 / irccounts.clients), ""); list_for_each_entry(acptr, &global_server_list, client_node) { @@ -136,7 +181,20 @@ void dump_flat_map(Client *client, Client *server, int length) continue; if (--cnt == 0) *buf = '`'; - sendnumeric(client, RPL_MAP, buf, length-2, acptr->name, acptr->server->users, ""); + + len = length - strlen(acptr->name) + 1; + if (len < 0) + len = 0; + if (len > 255) + len = 255; + + tbuf[len--] = '\0'; + while (len >= 0) + tbuf[len--] = '-'; + + sendnumeric(client, RPL_MAP, buf, acptr->name, tbuf, umax, acptr->server->users, + (lmax < 10) ? 4 : (lmax == 100) ? 6 : 5, + (acptr->server->users * 100.0 / irccounts.clients), ""); } } @@ -150,14 +208,23 @@ CMD_FUNC(cmd_map) { Client *acptr; int longest = strlen(me.name); + float avg_users; + + umax = 0; + lmax = 0; if (parc < 2) parv[1] = "*"; list_for_each_entry(acptr, &global_server_list, client_node) { + int perc = (acptr->server->users * 100 / irccounts.clients); if ((strlen(acptr->name) + acptr->hopcount * 2) > longest) longest = strlen(acptr->name) + acptr->hopcount * 2; + if (lmax < perc) + lmax = perc; + if (umax < dcount(acptr->server->users)) + umax = dcount(acptr->server->users); } if (longest > 60) @@ -169,5 +236,8 @@ CMD_FUNC(cmd_map) else dump_map(client, &me, "*", 0, longest); + avg_users = irccounts.clients * 1.0 / irccounts.servers; + sendnumeric(client, RPL_MAPUSERS, irccounts.servers, (irccounts.servers > 1 ? "s" : ""), irccounts.clients, + (irccounts.clients > 1 ? "s" : ""), avg_users); sendnumeric(client, RPL_MAPEND); } diff --git a/src/modules/message.c b/src/modules/message.c index 279df2c..62a1dbe 100644 --- a/src/modules/message.c +++ b/src/modules/message.c @@ -37,7 +37,7 @@ long CAP_MESSAGE_TAGS = 0; /**< Looked up at MOD_LOAD, may stay 0 if message-tag ModuleHeader MOD_HEADER = { "message", /* Name of module */ - "5.0", /* Version */ + "6.0.2", /* Version */ "private message and notice", /* Short description of module */ "UnrealIRCd Team", "unrealircd-6", @@ -318,7 +318,7 @@ void cmd_message(Client *client, MessageTag *recv_mtags, int parc, const char *p */ if (IsDead(client)) return; - if (!IsDead(client) && (sendtype != SEND_TYPE_NOTICE) && errmsg) + if (!IsDead(client) && (sendtype != SEND_TYPE_NOTICE) && !BadPtr(errmsg)) sendnumeric(client, ERR_CANNOTSENDTOCHAN, channel->name, errmsg, p2); continue; /* skip delivery to this target */ } @@ -423,7 +423,7 @@ void cmd_message(Client *client, MessageTag *recv_mtags, int parc, const char *p /* Message is discarded */ if (IsDead(client)) return; - if ((sendtype != SEND_TYPE_NOTICE) && errmsg) + if ((sendtype != SEND_TYPE_NOTICE) && !BadPtr(errmsg)) sendnumeric(client, ERR_CANTSENDTOUSER, target->name, errmsg); } else { diff --git a/src/modules/mode.c b/src/modules/mode.c index bb3a175..90fe1d9 100644 --- a/src/modules/mode.c +++ b/src/modules/mode.c @@ -370,28 +370,19 @@ void _do_mode(Channel *channel, Client *client, MessageTag *recv_mtags, int parc ":%s MODE %s %s %s", client->name, channel->name, modebuf, parabuf); - if (IsServer(client) && sendts != -1) + if (IsServer(client) || IsMe(client)) { sendto_server(client, 0, 0, mtags, ":%s MODE %s %s %s %lld", client->id, channel->name, modebuf, parabuf, - (long long)sendts); - } else - if (samode && IsMe(client)) - { - /* SAMODE is a special case: always send a TS of 0 (omitting TS==desync) */ - sendto_server(client, 0, 0, mtags, - ":%s MODE %s %s %s 0", - client->id, channel->name, - modebuf, parabuf); + (sendts != -1) ? (long long)sendts : 0LL); } else { sendto_server(client, 0, 0, mtags, ":%s MODE %s %s %s", client->id, channel->name, modebuf, parabuf); - /* tell them it's not a timestamp, in case the last param is a number. */ } if (MyConnect(client)) @@ -1159,6 +1150,22 @@ CMD_FUNC(_cmd_umode) goto def; case 't': case 'x': + /* set::anti-flood::vhost-flood */ + if (MyUser(client)) + { + if ((what == MODE_DEL) && !ValidatePermissionsForPath("immune:vhost-flood",client,NULL,NULL,NULL) && + flood_limit_exceeded(client, FLD_VHOST)) + { + /* Throttle... */ + if (!modex_err) + { + sendnotice(client, "*** Setting -%c too fast. Please try again later.", *m); + modex_err = 1; + } + break; + } + } + switch (UHOST_ALLOWED) { case UHALLOW_ALWAYS: diff --git a/src/modules/reputation.c b/src/modules/reputation.c index 0ca0064..7757eec 100644 --- a/src/modules/reputation.c +++ b/src/modules/reputation.c @@ -141,6 +141,7 @@ CMD_FUNC(reputationunperm); int reputation_whois(Client *client, Client *target, NameValuePrioList **list); int reputation_set_on_connect(Client *client); int reputation_pre_lconnect(Client *client); +int reputation_ip_change(Client *client, const char *oldip); int reputation_connect_extinfo(Client *client, NameValuePrioList **list); int reputation_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); int reputation_config_run(ConfigFile *cf, ConfigEntry *ce, int type); @@ -192,6 +193,7 @@ MOD_INIT() HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, reputation_config_run); HookAdd(modinfo->handle, HOOKTYPE_WHOIS, 0, reputation_whois); HookAdd(modinfo->handle, HOOKTYPE_HANDSHAKE, 0, reputation_set_on_connect); + HookAdd(modinfo->handle, HOOKTYPE_IP_CHANGE, 0, reputation_ip_change); HookAdd(modinfo->handle, HOOKTYPE_PRE_LOCAL_CONNECT, 2000000000, reputation_pre_lconnect); /* (prio: last) */ HookAdd(modinfo->handle, HOOKTYPE_REMOTE_CONNECT, -1000000000, reputation_set_on_connect); /* (prio: near-first) */ HookAdd(modinfo->handle, HOOKTYPE_CONNECT_EXTINFO, 0, reputation_connect_extinfo); /* (prio: near-first) */ @@ -804,6 +806,12 @@ int reputation_set_on_connect(Client *client) return 0; } +int reputation_ip_change(Client *client, const char *oldip) +{ + reputation_lookup_score_and_set(client); + return 0; +} + int reputation_pre_lconnect(Client *client) { /* User will likely be accepted. Inform other servers about the score diff --git a/src/modules/sapart.c b/src/modules/sapart.c index 6008389..936a65d 100644 --- a/src/modules/sapart.c +++ b/src/modules/sapart.c @@ -170,9 +170,9 @@ CMD_FUNC(cmd_sapart) if (comment) { snprintf(commentx, sizeof(commentx), "SAPart: %s", comment); - //sendnotice(target, "*** You were forced to part %s (%s)", request, commentx); + sendnotice(target, "*** You were forced to part %s (%s)", request, commentx); } else { - //sendnotice(target, "*** You were forced to part %s", request); + sendnotice(target, "*** You were forced to part %s", request); } parv[0] = target->name; // nick diff --git a/src/modules/setname.c b/src/modules/setname.c index b37106f..f1433c2 100644 --- a/src/modules/setname.c +++ b/src/modules/setname.c @@ -158,5 +158,5 @@ CMD_FUNC(cmd_setname) } free_message_tags(mtags); - RunHook(HOOKTYPE_REALNAME_CHANGED, client, oldinfo); + RunHook(HOOKTYPE_REALNAME_CHANGE, client, oldinfo); } diff --git a/src/modules/stats.c b/src/modules/stats.c index 513cfda..a7b5678 100644 --- a/src/modules/stats.c +++ b/src/modules/stats.c @@ -520,17 +520,17 @@ int stats_command(Client *client, const char *para) int stats_oper(Client *client, const char *para) { - ConfigItem_oper *oper_p; + ConfigItem_oper *o; ConfigItem_mask *m; - for (oper_p = conf_oper; oper_p; oper_p = oper_p->next) + for (o = conf_oper; o; o = o->next) { - for (m = oper_p->mask; m; m = m->next) + for (m = o->mask; m; m = m->next) { - sendnumeric(client, RPL_STATSOLINE, - 'O', m->mask, oper_p->name, - "-", - oper_p->class->name? oper_p->class->name : ""); + sendnumeric(client, RPL_STATSOLINE, + 'O', m->mask, o->name, + o->operclass ? o->operclass: "", + o->class->name ? o->class->name : ""); } } return 0; @@ -540,11 +540,20 @@ static char *stats_port_helper(ConfigItem_listen *listener) { static char buf[256]; - ircsnprintf(buf, sizeof(buf), "%s%s%s%s", + ircsnprintf(buf, sizeof(buf), "%s%s%s", (listener->options & LISTENER_CLIENTSONLY)? "clientsonly ": "", (listener->options & LISTENER_SERVERSONLY)? "serversonly ": "", - (listener->options & LISTENER_TLS)? "tls ": "", - !(listener->options & LISTENER_TLS)? "plaintext ": ""); + (listener->options & LISTENER_DEFER_ACCEPT)? "defer-accept ": ""); + + /* And one of these.. */ + if (listener->options & LISTENER_CONTROL) + strlcat(buf, "control ", sizeof(buf)); + else if (listener->socket_type == SOCKET_TYPE_UNIX) + ; + else if (listener->options & LISTENER_TLS) + strlcat(buf, "tls ", sizeof(buf)); + else + strlcat(buf, "plaintext ", sizeof(buf)); return buf; } @@ -558,13 +567,22 @@ int stats_port(Client *client, const char *para) continue; if ((listener->options & LISTENER_SERVERSONLY) && !ValidatePermissionsForPath("server:info:stats",client,NULL,NULL,NULL)) continue; - sendnotice(client, "*** Listener on %s:%i (%s): has %i client(s), options: %s %s", - listener->ip, - listener->port, - listener->ipv6 ? "IPv6" : "IPv4", - listener->clients, - stats_port_helper(listener), - listener->flag.temporary ? "[TEMPORARY]" : ""); + if (listener->socket_type == SOCKET_TYPE_UNIX) + { + sendnotice(client, "*** Listener on %s (UNIX): has %i client(s), options: %s %s", + listener->file, + listener->clients, + stats_port_helper(listener), + listener->flag.temporary ? "[TEMPORARY]" : ""); + } else { + sendnotice(client, "*** Listener on %s:%i (%s): has %i client(s), options: %s %s", + listener->ip, + listener->port, + listener->socket_type == SOCKET_TYPE_IPV6 ? "IPv6" : "IPv4", + listener->clients, + stats_port_helper(listener), + listener->flag.temporary ? "[TEMPORARY]" : ""); + } } return 0; } diff --git a/src/modules/svsmode.c b/src/modules/svsmode.c index 5fb4025..f534847 100644 --- a/src/modules/svsmode.c +++ b/src/modules/svsmode.c @@ -136,7 +136,7 @@ void unban_user(Client *client, Channel *channel, Client *acptr, char chmode) } else if (chmode != 'I' && *ban->banstr == '~' && (extban = findmod_by_bantype(ban->banstr, &nextbanstr))) { - if ((extban->options & EXTBOPT_CHSVSMODE) && (extban->is_banned_events & b->ban_check_types)) + if (extban->is_banned_events & b->ban_check_types) { b->banstr = nextbanstr; if (extban->is_banned(b)) @@ -176,7 +176,7 @@ void clear_bans(Client *client, Channel *channel, char chmode) bnext = ban->next; if (chmode != 'I' && (*ban->banstr == '~') && (extban = findmod_by_bantype(ban->banstr, NULL))) { - if (!(extban->options & EXTBOPT_CHSVSMODE)) + if (!(extban->is_banned_events & BANCHK_JOIN)) continue; } add_send_mode_param(channel, client, '-', chmode, ban->banstr); @@ -298,7 +298,7 @@ void channel_svsmode(Client *client, int parc, const char *parv[]) sendto_channel(channel, client, client, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s %s %s", client->name, channel->name, modebuf, parabuf); - sendto_server(NULL, 0, 0, mtags, ":%s MODE %s %s %s", client->id, channel->name, modebuf, parabuf); + sendto_server(NULL, 0, 0, mtags, ":%s MODE %s %s %s%s", client->id, channel->name, modebuf, parabuf, IsServer(client)?" 0":""); /* Activate this hook just like cmd_mode.c */ RunHook(HOOKTYPE_REMOTE_CHANMODE, client, channel, mtags, modebuf, parabuf, 0, 0, &destroy_channel); @@ -615,7 +615,7 @@ void add_send_mode_param(Channel *channel, Client *from, char what, char mode, c sendto_channel(channel, from, from, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s %s %s", from->name, channel->name, modebuf, parabuf); - sendto_server(NULL, 0, 0, mtags, ":%s MODE %s %s %s", from->id, channel->name, modebuf, parabuf); + sendto_server(NULL, 0, 0, mtags, ":%s MODE %s %s %s%s", from->id, channel->name, modebuf, parabuf, IsServer(from)?" 0":""); free_message_tags(mtags); send = 0; *parabuf = 0; diff --git a/src/modules/tkl.c b/src/modules/tkl.c index c8d93f1..7bc96b9 100644 --- a/src/modules/tkl.c +++ b/src/modules/tkl.c @@ -41,6 +41,7 @@ int tkl_config_test_except(ConfigFile *, ConfigEntry *, int, int *); int tkl_config_run_except(ConfigFile *, ConfigEntry *, int); int tkl_config_test_set(ConfigFile *, ConfigEntry *, int, int *); int tkl_config_run_set(ConfigFile *, ConfigEntry *, int); +int tkl_ip_change(Client *client, const char *oldip); CMD_FUNC(cmd_gline); CMD_FUNC(cmd_shun); CMD_FUNC(cmd_tempshun); @@ -213,6 +214,7 @@ MOD_INIT() HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkl_config_run_ban); HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkl_config_run_except); HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkl_config_run_set); + HookAdd(modinfo->handle, HOOKTYPE_IP_CHANGE, 2000000000, tkl_ip_change); CommandAdd(modinfo->handle, "GLINE", cmd_gline, 3, CMD_OPER); CommandAdd(modinfo->handle, "SHUN", cmd_shun, 3, CMD_OPER); CommandAdd(modinfo->handle, "TEMPSHUN", cmd_tempshun, 2, CMD_OPER); @@ -952,6 +954,12 @@ char *spamfilter_id(TKL *tk) return buf; } +int tkl_ip_change(Client *client, const char *oldip) +{ + check_banned(client, 0); + return 0; +} + /** GLINE - Global kline. ** Syntax: /gline [+|-]u@h mask time :reason ** @@ -4772,23 +4780,15 @@ int _match_spamfilter(Client *client, const char *str_in, int target, const char if (ret) { - /* We have a match! */ - char destinationbuf[48]; - - if (destination) { - destinationbuf[0] = ' '; - strlcpy(destinationbuf+1, destination, sizeof(destinationbuf)-1); /* cut it off */ - } else - destinationbuf[0] = '\0'; - - /* Hold on.. perhaps it's on the exceptions list... */ + /* We have a match! But.. perhaps it's on the exceptions list? */ if (!winner_tkl && destination && target_is_spamexcept(destination)) return 0; /* No problem! */ unreal_log(ULOG_INFO, "tkl", "SPAMFILTER_MATCH", client, - "[Spamfilter] $client.details matches filter '$tkl': [cmd: $command$destination: '$str'] [reason: $tkl.reason] [action: $tkl.ban_action]", + "[Spamfilter] $client.details matches filter '$tkl': [cmd: $command$_space$destination: '$str'] [reason: $tkl.reason] [action: $tkl.ban_action]", log_data_tkl("tkl", tkl), log_data_string("command", cmd), + log_data_string("_space", destination ? " " : ""), log_data_string("destination", destination ? destination : ""), log_data_string("str", str)); diff --git a/src/modules/watch-backend.c b/src/modules/watch-backend.c index 40f73c4..708ab09 100644 --- a/src/modules/watch-backend.c +++ b/src/modules/watch-backend.c @@ -29,9 +29,9 @@ ModDataInfo *watchCounterMD; ModDataInfo *watchListMD; -static Watch *watchTable[WATCH_HASH_TABLE_SIZE]; -static int watch_initialized = 0; -static char siphashkey_watch[SIPHASH_KEY_LENGTH]; + +static Watch **watchTable = NULL; +static char *siphashkey_watch = NULL; void dummy_free(ModData *md); void watch_free(ModData *md); @@ -47,8 +47,8 @@ uint64_t hash_watch_nick_name(const char *name); ModuleHeader MOD_HEADER = { "watch-backend", - "5.0", - "backend for /watch", + "6.0.3", + "backend for /WATCH", "UnrealIRCd Team", "unrealircd-6", }; @@ -65,20 +65,28 @@ MOD_TEST() return MOD_SUCCESS; } +void watch_generic_free(ModData *m) +{ + safe_free(m->ptr); +} + MOD_INIT() { ModDataInfo mreq; MARK_AS_OFFICIAL_MODULE(modinfo); ModuleSetOptions(modinfo->handle, MOD_OPT_PERM_RELOADABLE, 1); /* or do a complex memory freeing algorithm instead */ - - if (!watch_initialized) + + LoadPersistentPointer(modinfo, siphashkey_watch, watch_generic_free); + if (siphashkey_watch == NULL) { - memset(watchTable, 0, sizeof(watchTable)); + siphashkey_watch = safe_alloc(SIPHASH_KEY_LENGTH); siphash_generate_key(siphashkey_watch); - watch_initialized = 1; } - + LoadPersistentPointer(modinfo, watchTable, watch_generic_free); + if (watchTable == NULL) + watchTable = safe_alloc(sizeof(Watch) * WATCH_HASH_TABLE_SIZE); + memset(&mreq, 0 , sizeof(mreq)); mreq.type = MODDATATYPE_LOCAL_CLIENT; mreq.name = "watchCount", @@ -113,6 +121,8 @@ MOD_LOAD() MOD_UNLOAD() { + SavePersistentPointer(modinfo, siphashkey_watch); + SavePersistentPointer(modinfo, watchTable); return MOD_SUCCESS; } @@ -151,13 +161,13 @@ int _watch_add(char *nick, Client *client, int flags) hashv = hash_watch_nick_name(nick); /* Find the right nick (header) in the bucket, or NULL... */ - if ((watch = (Watch *)watchTable[hashv])) + if ((watch = watchTable[hashv])) while (watch && mycmp(watch->nick, nick)) watch = watch->hnext; /* If found NULL (no header for this nick), make one... */ if (!watch) { - watch = (Watch *)safe_alloc(sizeof(Watch)+strlen(nick)); + watch = safe_alloc(sizeof(Watch)+strlen(nick)); watch->lasttime = timeofday; strcpy(watch->nick, nick); @@ -203,7 +213,7 @@ int _watch_check(Client *client, int event, int (*watch_notify)(Client *client, hashv = hash_watch_nick_name(client->name); /* Find the right header in this bucket */ - if ((watch = (Watch *)watchTable[hashv])) + if ((watch = watchTable[hashv])) while (watch && mycmp(watch->nick, client->name)) watch = watch->hnext; if (!watch) @@ -231,7 +241,7 @@ Watch *_watch_get(char *nick) hashv = hash_watch_nick_name(nick); - if ((watch = (Watch *)watchTable[hashv])) + if ((watch = watchTable[hashv])) while (watch && mycmp(watch->nick, nick)) watch = watch->hnext; diff --git a/src/modules/webirc.c b/src/modules/webirc.c index 373761d..da4093f 100644 --- a/src/modules/webirc.c +++ b/src/modules/webirc.c @@ -336,6 +336,7 @@ ConfigItem_webirc *find_webirc(Client *client, const char *password, WEBIRCType /* Does the CGI:IRC host spoofing work */ void dowebirc(Client *client, const char *ip, const char *host, const char *options) { + char oldip[64]; char scratch[64]; if (IsWEBIRC(client)) @@ -357,6 +358,7 @@ void dowebirc(Client *client, const char *ip, const char *host, const char *opti } /* STEP 2: Update GetIP() */ + strlcpy(oldip, client->ip, sizeof(oldip)); safe_strdup(client->ip, ip); /* STEP 3: Update client->local->hostp */ @@ -397,15 +399,7 @@ void dowebirc(Client *client, const char *ip, const char *host, const char *opti } } - /* blacklist_start_check() */ - if (RCallbacks[CALLBACKTYPE_BLACKLIST_CHECK] != NULL) - RCallbacks[CALLBACKTYPE_BLACKLIST_CHECK]->func.intfunc(client); - - /* Check (g)zlines right now; these are normally checked upon accept(), - * but since we know the IP only now after PASS/WEBIRC, we have to check - * here again... - */ - check_banned(client, 0); + RunHook(HOOKTYPE_IP_CHANGE, client, oldip); } /* WEBIRC "cgiirc" [:option1 [option2...]]*/ diff --git a/src/modules/websocket.c b/src/modules/websocket.c index 73c9fcc..111b0e4 100644 --- a/src/modules/websocket.c +++ b/src/modules/websocket.c @@ -686,6 +686,9 @@ int websocket_handshake_valid(Client *client) } if (WSU(client)->forwarded) { + struct HTTPForwardedHeader *forwarded; + char oldip[64]; + /* check for source ip */ if (BadPtr(client->local->listener->websocket_forward) || !websocket_ip_compare(client->local->listener->websocket_forward, client->ip)) { @@ -694,7 +697,6 @@ int websocket_handshake_valid(Client *client) return 0; } /* parse the header */ - struct HTTPForwardedHeader *forwarded; forwarded = websocket_parse_forwarded_header(WSU(client)->forwarded); /* check header values */ if (!is_valid_ip(forwarded->ip)) @@ -705,6 +707,7 @@ int websocket_handshake_valid(Client *client) } /* store data */ WSU(client)->secure = forwarded->secure; + strlcpy(oldip, client->ip, sizeof(oldip)); safe_strdup(client->ip, forwarded->ip); /* Update client->local->hostp */ strlcpy(client->local->sockhost, forwarded->ip, sizeof(client->local->sockhost)); /* in case dns lookup fails or is disabled */ @@ -733,15 +736,7 @@ int websocket_handshake_valid(Client *client) /* Race condition detected, DNS has been done, continue with auth */ } } - /* blacklist_start_check() */ - if (RCallbacks[CALLBACKTYPE_BLACKLIST_CHECK] != NULL) - RCallbacks[CALLBACKTYPE_BLACKLIST_CHECK]->func.intfunc(client); - - /* Check (g)zlines right now; these are normally checked upon accept(), - * but since we know the IP only now after PASS/WEBIRC, we have to check - * here again... - */ - check_banned(client, 0); + RunHook(HOOKTYPE_IP_CHANGE, client, oldip); } return 1; } diff --git a/src/modules/whox.c b/src/modules/whox.c index ca4239e..67ab533 100644 --- a/src/modules/whox.c +++ b/src/modules/whox.c @@ -46,6 +46,7 @@ ModuleHeader MOD_HEADER #define WMATCH_ACCOUNT 0x0040 #define WMATCH_IP 0x0080 #define WMATCH_MODES 0x0100 +#define WMATCH_CONTIME 0x0200 #define RPL_WHOSPCRPL 354 @@ -69,6 +70,8 @@ struct who_format const char *querytype; int show_realhost; int show_ip; + time_t contimemin; + time_t contimemax; }; /* Global variables */ @@ -229,6 +232,7 @@ CMD_FUNC(cmd_whox) case 's': fmt.matchsel |= WMATCH_SERVER; continue; case 'a': fmt.matchsel |= WMATCH_ACCOUNT; continue; case 'm': fmt.matchsel |= WMATCH_MODES; continue; + case 't': fmt.matchsel |= WMATCH_CONTIME; continue; case 'R': if (IsOper(client)) fmt.show_realhost = 1; @@ -336,6 +340,28 @@ CMD_FUNC(cmd_whox) } } + /* match connect time */ + if (fmt.matchsel & WMATCH_CONTIME) + { + char *s = mask; + time_t currenttime = TStime(); + + fmt.contimemin = 0; + fmt.contimemax = 0; + + switch (*s) + { + case '<': + if (*s++) + fmt.contimemin = currenttime - config_checkval(s, CFG_TIME); + break; + case '>': + if (*s++) + fmt.contimemax = currenttime - config_checkval(s, CFG_TIME); + break; + } + } + /* '/who #some_channel' */ if (IsChannelName(mask)) { @@ -449,6 +475,16 @@ static int do_match(Client *client, Client *acptr, char *mask, struct who_format } } + /* match connect time */ + if (IsMatch(fmt, WMATCH_CONTIME) && MyConnect(acptr) && (fmt->contimemin || fmt->contimemax)) + { + if (fmt->contimemin && (acptr->local->creationtime > fmt->contimemin)) + return 1; + + if (fmt->contimemax && (acptr->local->creationtime < fmt->contimemax)) + return 1; + } + return 0; } diff --git a/src/parse.c b/src/parse.c index 473453a..2b9f884 100644 --- a/src/parse.c +++ b/src/parse.c @@ -104,7 +104,9 @@ void parse_client_queued(Client *client) return; /* we delay processing of data until identd has replied */ if (!IsUser(client) && !IsServer(client) && (iConf.handshake_delay > 0) && - !IsNoHandshakeDelay(client) && (TStime() - client->local->creationtime < iConf.handshake_delay)) + !IsNoHandshakeDelay(client) && + !IsControl(client) && + (TStime() - client->local->creationtime < iConf.handshake_delay)) { return; /* we delay processing of data until set::handshake-delay is reached */ } @@ -368,6 +370,8 @@ static void parse2(Client *cptr, Client **fromptr, MessageTag *mtags, int mtags_ flags |= CMD_VIRUS; if (IsOper(from)) flags |= CMD_OPER; + if (IsControl(from)) + flags |= CMD_CONTROL; cmptr = find_command(ch, flags); if (!cmptr || !(cmptr->flags & CMD_NOLAG)) { @@ -376,6 +380,12 @@ static void parse2(Client *cptr, Client **fromptr, MessageTag *mtags, int mtags_ } if (!cmptr) { + if (IsControl(from)) + { + sendto_one(from, NULL, "ERROR UNKNOWN_COMMAND: %s", ch); + sendto_one(from, NULL, "END 1"); + return; + } /* Don't send error messages in response to NOTICEs * in pre-connection state. */ diff --git a/src/proc_io_client.c b/src/proc_io_client.c new file mode 100644 index 0000000..69caa24 --- /dev/null +++ b/src/proc_io_client.c @@ -0,0 +1,199 @@ +/************************************************************************ + * UnrealIRCd - Unreal Internet Relay Chat Daemon - src/proc_io_client.c + * (c) 2022- Bram Matthys and 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. + */ + +/** @file + * @brief Inter-process I/O + */ +#include "unrealircd.h" + +int procio_client_connect(const char *file) +{ + int fd; + struct sockaddr_un addr; + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + { +#ifdef _WIN32 + fprintf(stderr, "Your Windows version does not support UNIX sockets, " + "so cannot communicate to UnrealIRCd.\n" + "Windows 10 version 1803 (April 2018) or later is needed.\n"); +#else + fprintf(stderr, "Cannot communicate to UnrealIRCd: %s\n" + "Perhaps your operating system does not support UNIX Sockets?\n", + strerror(ERRNO)); +#endif + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strlcpy(addr.sun_path, file, sizeof(addr.sun_path)); + + if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) + { + fprintf(stderr, "Could not connect to '%s': %s\n", + CONTROLFILE, strerror(errno)); + fprintf(stderr, "The IRC server does not appear to be running.\n"); + close(fd); + return -1; + } + + return fd; +} + +int procio_send(int fd, const char *command) +{ + char buf[512]; + int n; + snprintf(buf, sizeof(buf), "%s\r\n", command); + n = strlen(buf); + if (send(fd, buf, n, 0) != n) + return 0; + return 1; +} + +const char *recolor_logs(const char *str) +{ + static char retbuf[2048]; + char buf[2048], *p; + const char *color = NULL; + + strlcpy(buf, str, sizeof(buf)); + p = strchr(buf, ' '); + if ((*str != '[') || !p) + return str; + *p++ = '\0'; + + if (!strcmp(buf, "[debug]")) + color = log_level_terminal_color(ULOG_DEBUG); + else if (!strcmp(buf, "[info]")) + color = log_level_terminal_color(ULOG_INFO); + else if (!strcmp(buf, "[warning]")) + color = log_level_terminal_color(ULOG_WARNING); + else if (!strcmp(buf, "[error]")) + color = log_level_terminal_color(ULOG_ERROR); + else if (!strcmp(buf, "[fatal]")) + color = log_level_terminal_color(ULOG_FATAL); + else + color = log_level_terminal_color(ULOG_INVALID); + + snprintf(retbuf, sizeof(retbuf), "%s%s%s %s", + color, buf, TERMINAL_COLOR_RESET, p); + return retbuf; +} + +const char *recolor_split(const char *str) +{ + static char retbuf[2048]; + char buf[2048], *p; + const char *color = NULL; + + strlcpy(buf, str, sizeof(buf)); + p = strchr(buf, ' '); + if (!p) + return str; + *p++ = '\0'; + + snprintf(retbuf, sizeof(retbuf), "%s%s %s%s%s", + "\033[92m", buf, + "\033[93m", p, + TERMINAL_COLOR_RESET); + return retbuf; +} + +int procio_client(const char *command, int auto_color_logs) +{ + int fd; + char buf[READBUFSIZE]; + int n; + dbuf queue; + + if (auto_color_logs && !terminal_supports_color()) + auto_color_logs = 0; + + fd = procio_client_connect(CONTROLFILE); + if (fd < 0) + return -1; + + /* Expect the welcome message */ + memset(buf, 0, sizeof(buf)); + n = recv(fd, buf, sizeof(buf), 0); + if ((n < 0) || strncmp(buf, "READY", 4)) + { + fprintf(stderr, "Error while communicating to IRCd via '%s': %s\n" + "Maybe the IRC server is not running?\n", + CONTROLFILE, strerror(errno)); + close(fd); + return -1; + } + + if (!procio_send(fd, command)) + { + fprintf(stderr, "Error while sending command to IRCd via '%s'. Strange!\n", + CONTROLFILE); + close(fd); + return -1; + } + + *buf = '\0'; + dbuf_queue_init(&queue); + while(1) + { + n = recv(fd, buf, sizeof(buf)-1, 0); + if (n <= 0) + break; + buf[n] = '\0'; /* terminate the string */ + dbuf_put(&queue, buf, n); + + /* And try to read all complete lines: */ + do + { + n = dbuf_getmsg(&queue, buf); + if (n > 0) + { + if (!strncmp(buf, "REPLY ", 6)) + { + char *reply = buf+6; + if (auto_color_logs == 0) + printf("%s\n", reply); + else if (auto_color_logs == 1) + printf("%s\n", recolor_logs(reply)); + else + printf("%s\n", recolor_split(reply)); + } else + if (!strncmp(buf, "END ", 4)) + { + int exitcode = atoi(buf+4); + close(fd); + return exitcode; + } + } + } while(n > 0); + } + + /* IRCd hung up without saying goodbye, possibly problematic, + * or at least we cannot determine, so exit with status 66. + */ + close(fd); + return 66; +} diff --git a/src/proc_io_server.c b/src/proc_io_server.c new file mode 100644 index 0000000..35e1506 --- /dev/null +++ b/src/proc_io_server.c @@ -0,0 +1,170 @@ +/************************************************************************ + * UnrealIRCd - Unreal Internet Relay Chat Daemon - src/proc_io_server.c + * (c) 2022- Bram Matthys and 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. + */ + +/** @file + * @brief Inter-process I/O + */ +#include "unrealircd.h" +#include + +CMD_FUNC(procio_status); +CMD_FUNC(procio_modules); +CMD_FUNC(procio_rehash); +CMD_FUNC(procio_exit); +CMD_FUNC(procio_help); + +/** Create the unrealircd.ctl socket (server-side) */ +void add_proc_io_server(void) +{ + ConfigItem_listen *listener; + +#ifdef _WIN32 + /* Ignore silently on Windows versions older than W10 build 17061 */ + if (!unix_sockets_capable()) + return; +#endif + listener = safe_alloc(sizeof(ConfigItem_listen)); + safe_strdup(listener->file, CONTROLFILE); + listener->socket_type = SOCKET_TYPE_UNIX; + listener->options = LISTENER_CONTROL; + listener->fd = -1; + AddListItem(listener, conf_listen); + if (add_listener(listener) == -1) + exit(-1); + CommandAdd(NULL, "STATUS", procio_status, MAXPARA, CMD_CONTROL); + CommandAdd(NULL, "MODULES", procio_modules, MAXPARA, CMD_CONTROL); + CommandAdd(NULL, "REHASH", procio_rehash, MAXPARA, CMD_CONTROL); + CommandAdd(NULL, "EXIT", procio_exit, MAXPARA, CMD_CONTROL); + CommandAdd(NULL, "HELP", procio_help, MAXPARA, CMD_CONTROL); +} + +/** Start of "control channel" client handshake - this is minimal + * @param client The client + */ +void start_of_control_client_handshake(Client *client) +{ + sendto_one(client, NULL, "READY %s %s", me.name, version); + fd_setselect(client->local->fd, FD_SELECT_READ, read_packet, client); +} + +CMD_FUNC(procio_status) +{ + sendto_one(client, NULL, "REPLY servername %s", me.name); + sendto_one(client, NULL, "REPLY unrealircd_version %s", version); + sendto_one(client, NULL, "REPLY libssl_version %s", SSLeay_version(SSLEAY_VERSION)); + sendto_one(client, NULL, "REPLY libsodium_version %s", sodium_version_string()); +#ifdef USE_LIBCURL + sendto_one(client, NULL, "REPLY libcurl_version %s", curl_version()); +#endif + sendto_one(client, NULL, "REPLY libcares_version %s", ares_version(NULL)); + sendto_one(client, NULL, "REPLY libpcre2_version %s", pcre2_version()); + sendto_one(client, NULL, "REPLY global_clients %ld", (long)irccounts.clients); + sendto_one(client, NULL, "REPLY local_clients %ld", (long)irccounts.me_clients); + sendto_one(client, NULL, "REPLY operators %ld", (long)irccounts.operators); + sendto_one(client, NULL, "REPLY servers %ld", (long)irccounts.servers); + sendto_one(client, NULL, "REPLY channels %ld", (long)irccounts.channels); + sendto_one(client, NULL, "END 0"); +} + +extern MODVAR Module *Modules; +CMD_FUNC(procio_modules) +{ + char tmp[1024]; + Module *m; + + for (m = Modules; m; m = m->next) + { + tmp[0] = '\0'; + if (m->flags & MODFLAG_DELAYED) + strlcat(tmp, "[Unloading] ", sizeof(tmp)); + if (m->options & MOD_OPT_PERM_RELOADABLE) + strlcat(tmp, "[PERM-BUT-RELOADABLE] ", sizeof(tmp)); + if (m->options & MOD_OPT_PERM) + strlcat(tmp, "[PERM] ", sizeof(tmp)); + if (!(m->options & MOD_OPT_OFFICIAL)) + strlcat(tmp, "[3RD] ", sizeof(tmp)); + sendto_one(client, NULL, "REPLY %s %s - %s - by %s %s", + m->header->name, + m->header->version, + m->header->description, + m->header->author, + tmp); + } + sendto_one(client, NULL, "END 0"); +} + +CMD_FUNC(procio_rehash) +{ + if (loop.rehashing) + { + sendto_one(client, NULL, "REPLY ERROR: A rehash is already in progress"); + sendto_one(client, NULL, "END 1"); + return; + } + + + if (parv[1] && !strcmp(parv[1], "-tls")) + { + int ret; + SetMonitorRehash(client); + unreal_log(ULOG_INFO, "config", "CONFIG_RELOAD_TLS", NULL, "Reloading all TLS related data (./unrealircd reloadtls)"); + ret = reinit_tls(); + sendto_one(client, NULL, "END %d", ret == 0 ? -1 : 0); + ClearMonitorRehash(client); + } else { + SetMonitorRehash(client); + request_rehash(client); + /* completion will go via procio_post_rehash() */ + } +} + +CMD_FUNC(procio_exit) +{ + sendto_one(client, NULL, "END 0"); + exit_client(client, NULL, ""); +} + +CMD_FUNC(procio_help) +{ + sendto_one(client, NULL, "REPLY Commands available:"); + sendto_one(client, NULL, "REPLY EXIT"); + sendto_one(client, NULL, "REPLY HELP"); + sendto_one(client, NULL, "REPLY REHASH"); + sendto_one(client, NULL, "REPLY STATUS"); + sendto_one(client, NULL, "REPLY MODULES"); + sendto_one(client, NULL, "END 0"); +} + +/** Called upon REHASH completion (with or without failure) */ +void procio_post_rehash(int failure) +{ + Client *client; + + list_for_each_entry(client, &control_list, lclient_node) + { + if (IsMonitorRehash(client)) + { + sendto_one(client, NULL, "END %d", failure); + ClearMonitorRehash(client); + } + } +} diff --git a/src/send.c b/src/send.c index 13fbaf5..0abdd61 100644 --- a/src/send.c +++ b/src/send.c @@ -218,7 +218,7 @@ void vsendto_one(Client *to, MessageTag *mtags, const char *pattern, va_list vl) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" #endif - ircvsnprintf(sendbuf, sizeof(sendbuf), pattern, vl); + ircvsnprintf(sendbuf, sizeof(sendbuf)-3, pattern, vl); #if defined(__GNUC__) #pragma GCC diagnostic pop #endif @@ -229,7 +229,7 @@ void vsendto_one(Client *to, MessageTag *mtags, const char *pattern, va_list vl) sendbufto_one(to, sendbuf, 0); } else { /* Message tags need to be prepended */ - snprintf(sendbuf2, sizeof(sendbuf2), "@%s %s", mtags_str, sendbuf); + snprintf(sendbuf2, sizeof(sendbuf2)-3, "@%s %s", mtags_str, sendbuf); sendbufto_one(to, sendbuf2, 0); } } @@ -339,10 +339,10 @@ void sendbufto_one(Client *to, char *msg, unsigned int quick) if (IsMe(to)) { - char tmp_msg[500], *p; + char tmp_msg[500]; - p = strchr(msg, '\r'); - if (p) *p = '\0'; + strlcpy(tmp_msg, msg, sizeof(tmp_msg)); + stripcrlf(tmp_msg); unreal_log(ULOG_WARNING, "send", "SENDBUFTO_ONE_ME_MESSAGE", to, "Trying to send data to myself: $buf", log_data_string("buf", tmp_msg)); @@ -395,7 +395,10 @@ void sendbufto_one(Client *to, char *msg, unsigned int quick) * a bad idea, CPU-wise. So now we just mark the client indicating * that there is data to send. */ - mark_data_to_send(to); + if (IsControl(to)) + send_queued(to); /* send this one ASAP */ + else + mark_data_to_send(to); } /** A single function to send data to a channel. diff --git a/src/serv.c b/src/serv.c index 6fc1377..8f8bdd9 100644 --- a/src/serv.c +++ b/src/serv.c @@ -716,62 +716,77 @@ CMD_FUNC(cmd_restart) /** Send short message of the day to the client */ void short_motd(Client *client) { - ConfigItem_tld *tld; - MOTDFile *themotd; - MOTDLine *motdline; - struct tm *tm; - char is_short; + ConfigItem_tld *tld; + MOTDFile *themotd; + MOTDLine *motdline; + struct tm *tm; + char is_short; - tm = NULL; - is_short = 1; + tm = NULL; + is_short = 1; - tld = find_tld(client); + tld = find_tld(client); - /* + /* * Try different sources of short MOTDs, falling back to the * long MOTD. - */ - themotd = &smotd; - if (tld && tld->smotd.lines) - themotd = &tld->smotd; + */ + themotd = &smotd; + if (tld && tld->smotd.lines) + themotd = &tld->smotd; - /* try long MOTDs */ - if (!themotd->lines) - { - is_short = 0; - if (tld && tld->motd.lines) - themotd = &tld->motd; - else - themotd = &motd; - } + /* try long MOTDs */ + if (!themotd->lines) + { + is_short = 0; + if (tld && tld->motd.lines) + themotd = &tld->motd; + else + themotd = &motd; + } - if (!themotd->lines) - { - sendnumeric(client, ERR_NOMOTD); - return; - } - if (themotd->last_modified.tm_year) - { - tm = &themotd->last_modified; /* for readability */ - sendnumeric(client, RPL_MOTDSTART, me.name); - sendnumericfmt(client, RPL_MOTD, ":- %d/%d/%d %d:%02d", tm->tm_mday, tm->tm_mon + 1, - 1900 + tm->tm_year, tm->tm_hour, tm->tm_min); - } - if (is_short) - { - sendnumeric(client, RPL_MOTD, "This is the short MOTD. To view the complete MOTD type /motd"); - sendnumeric(client, RPL_MOTD, ""); - } + if (!themotd->lines) + { + sendnumeric(client, ERR_NOMOTD); + return; + } + if (themotd->last_modified.tm_year) + { + tm = &themotd->last_modified; /* for readability */ + sendnumeric(client, RPL_MOTDSTART, me.name); + sendnumericfmt(client, RPL_MOTD, ":- %d/%d/%d %d:%02d", tm->tm_mday, tm->tm_mon + 1, + 1900 + tm->tm_year, tm->tm_hour, tm->tm_min); + } + if (is_short) + { + sendnumeric(client, RPL_MOTD, "This is the short MOTD. To view the complete MOTD type /motd"); + sendnumeric(client, RPL_MOTD, ""); + } - motdline = NULL; - if (themotd) - motdline = themotd->lines; - while (motdline) - { - sendnumeric(client, RPL_MOTD, motdline->line); - motdline = motdline->next; - } - sendnumeric(client, RPL_ENDOFMOTD); + motdline = NULL; + if (themotd) + motdline = themotd->lines; + while (motdline) + { + sendnumeric(client, RPL_MOTD, motdline->line); + motdline = motdline->next; + } + + if (!is_short) + { + /* If the admin does not use a short MOTD then we append the SVSMOTD here... + * If we did show a short motd then we don't append SVSMOTD, + * since they want to keep it short. + */ + motdline = svsmotd.lines; + while (motdline) + { + sendnumeric(client, RPL_MOTD, motdline->line); + motdline = motdline->next; + } + } + + sendnumeric(client, RPL_ENDOFMOTD); } /** Read motd-like file, used for rules/motd/botmotd/opermotd/etc. diff --git a/src/socket.c b/src/socket.c index cb4d508..0191214 100644 --- a/src/socket.c +++ b/src/socket.c @@ -32,7 +32,7 @@ int OpenFiles = 0; /* GLOBAL - number of files currently open */ int readcalls = 0; void completed_connection(int, int, void *); -void set_sock_opts(int, Client *, int); +void set_sock_opts(int, Client *, SocketType); void set_ipv6_opts(int); void close_listener(ConfigItem_listen *listener); static char readbuf[BUFSIZE]; @@ -41,6 +41,7 @@ extern char *version; MODVAR time_t last_allinuse = 0; void start_of_normal_client_handshake(Client *client); +extern void start_of_control_client_handshake(Client *client); void proceed_normal_client_handshake(Client *client, struct hostent *he); /** Close all connections - only used when we terminate the server (eg: /DIE or SIGTERM) */ @@ -72,6 +73,15 @@ void close_connections(void) } } + list_for_each_entry(client, &control_list, lclient_node) + { + if (client->local->fd >= 0) + { + fd_close(client->local->fd); + client->local->fd = -2; + } + } + close_unbound_listeners(); OpenFiles = 0; @@ -101,10 +111,17 @@ static void listener_accept(int listener_fd, int revents, void *data) * Of course the underlying cause of this issue should be investigated, as this * is very much a workaround. */ - unreal_log(ULOG_FATAL, "listen", "ACCEPT_ERROR", NULL, "Cannot accept incoming connection on IP \"$listen_ip\" port $listen_port: $socket_error", - log_data_socket_error(listener->fd), - log_data_string("listen_ip", listener->ip), - log_data_integer("listen_port", listener->port)); + if (listener->file) + { + unreal_log(ULOG_FATAL, "listen", "ACCEPT_ERROR", NULL, "Cannot accept incoming connection on file $file: $socket_error", + log_data_socket_error(listener->fd), + log_data_string("file", listener->file)); + } else { + unreal_log(ULOG_FATAL, "listen", "ACCEPT_ERROR", NULL, "Cannot accept incoming connection on IP \"$listen_ip\" port $listen_port: $socket_error", + log_data_socket_error(listener->fd), + log_data_string("listen_ip", listener->ip), + log_data_integer("listen_port", listener->port)); + } close_listener(listener); start_listeners(); } @@ -113,45 +130,67 @@ static void listener_accept(int listener_fd, int revents, void *data) ircstats.is_ac++; - set_sock_opts(cli_fd, NULL, listener->ipv6); + set_sock_opts(cli_fd, NULL, listener->socket_type); - if ((++OpenFiles >= maxclients) || (cli_fd >= maxclients)) + /* Allow connections to the control socket, even if maxclients is reached */ + if (listener->options & LISTENER_CONTROL) { - ircstats.is_ref++; - if (last_allinuse < TStime() - 15) + /* ... but not unlimited ;) */ + if ((++OpenFiles >= maxclients+(CLIENTS_RESERVE/2)) || (cli_fd >= maxclients+(CLIENTS_RESERVE/2))) { - unreal_log(ULOG_FATAL, "listen", "ACCEPT_ERROR_MAXCLIENTS", NULL, "Cannot accept incoming connection on IP \"$listen_ip\" port $listen_port: All connections in use", - log_data_string("listen_ip", listener->ip), - log_data_integer("listen_port", listener->port)); - last_allinuse = TStime(); + ircstats.is_ref++; + if (last_allinuse < TStime() - 15) + { + unreal_log(ULOG_FATAL, "listen", "ACCEPT_ERROR_MAXCLIENTS", NULL, "Cannot accept incoming connection on file $file: All connections in use", + log_data_string("file", listener->file)); + last_allinuse = TStime(); + } + fd_close(cli_fd); + --OpenFiles; + return; } + } else + { + if ((++OpenFiles >= maxclients) || (cli_fd >= maxclients)) + { + ircstats.is_ref++; + if (last_allinuse < TStime() - 15) + { + if (listener->file) + { + unreal_log(ULOG_FATAL, "listen", "ACCEPT_ERROR_MAXCLIENTS", NULL, "Cannot accept incoming connection on file $file: All connections in use", + log_data_string("file", listener->file)); + } else { + unreal_log(ULOG_FATAL, "listen", "ACCEPT_ERROR_MAXCLIENTS", NULL, "Cannot accept incoming connection on IP \"$listen_ip\" port $listen_port: All connections in use", + log_data_string("listen_ip", listener->ip), + log_data_integer("listen_port", listener->port)); + } + last_allinuse = TStime(); + } - (void)send(cli_fd, "ERROR :All connections in use\r\n", 31, 0); + (void)send(cli_fd, "ERROR :All connections in use\r\n", 31, 0); - fd_close(cli_fd); - --OpenFiles; - return; + fd_close(cli_fd); + --OpenFiles; + return; + } } /* add_connection() may fail. we just don't care. */ add_connection(listener, cli_fd); } -/** Create a listener port. - * @param listener The listen { } block configuration - * @param ip IP address to bind on - * @param port Port to bind on - * @param ipv6 IPv6 (1) or IPv4 (0) - * @returns 0 on success and <0 on error. Yeah, confusing. - */ -int unreal_listen(ConfigItem_listen *listener, char *ip, int port, int ipv6) +int unreal_listen_inet(ConfigItem_listen *listener) { + const char *ip = listener->ip; + int port = listener->port; + if (BadPtr(ip)) ip = "*"; - + if (*ip == '*') { - if (ipv6) + if (listener->socket_type == SOCKET_TYPE_IPV6) ip = "::"; else ip = "0.0.0.0"; @@ -160,11 +199,11 @@ int unreal_listen(ConfigItem_listen *listener, char *ip, int port, int ipv6) /* At first, open a new socket */ if (listener->fd >= 0) abort(); /* Socket already exists but we are asked to create and listen on one. Bad! */ - + if (port == 0) abort(); /* Impossible as well, right? */ - listener->fd = fd_socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0, "Listener socket"); + listener->fd = fd_socket(listener->socket_type == SOCKET_TYPE_IPV6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0, "Listener socket"); if (listener->fd < 0) { unreal_log(ULOG_FATAL, "listen", "LISTEN_SOCKET_ERROR", NULL, @@ -187,9 +226,9 @@ int unreal_listen(ConfigItem_listen *listener, char *ip, int port, int ipv6) return -1; } - set_sock_opts(listener->fd, NULL, ipv6); + set_sock_opts(listener->fd, NULL, listener->socket_type); - if (!unreal_bind(listener->fd, ip, port, ipv6)) + if (!unreal_bind(listener->fd, ip, port, listener->socket_type)) { unreal_log(ULOG_FATAL, "listen", "LISTEN_BIND_ERROR", NULL, "Could not listen on IP \"$listen_ip\" on port $listen_port: $socket_error", @@ -240,23 +279,95 @@ int unreal_listen(ConfigItem_listen *listener, char *ip, int port, int ipv6) return 0; } -/** Activate a listen { } block */ -int add_listener(ConfigItem_listen *conf) +int unreal_listen_unix(ConfigItem_listen *listener) { - if (unreal_listen(conf, conf->ip, conf->port, conf->ipv6)) + if (listener->socket_type != SOCKET_TYPE_UNIX) + abort(); /* "impossible" */ + + /* At first, open a new socket */ + if (listener->fd >= 0) + abort(); /* Socket already exists but we are asked to create and listen on one. Bad! */ + + listener->fd = fd_socket(AF_UNIX, SOCK_STREAM, 0, "Listener socket (UNIX)"); + if (listener->fd < 0) { - /* Error is already handled upstream */ - conf->fd = -2; + unreal_log(ULOG_FATAL, "listen", "LISTEN_SOCKET_ERROR", NULL, + "Could not create UNIX domain socket for $file: $socket_error", + log_data_socket_error(-1), + log_data_string("file", listener->file)); + return -1; } - if (conf->fd >= 0) + if (++OpenFiles >= maxclients) { - conf->options |= LISTENER_BOUND; + unreal_log(ULOG_FATAL, "listen", "LISTEN_ERROR_MAXCLIENTS", NULL, + "Could not create UNIX domain socket for $file: all connections in use", + log_data_string("file", listener->file)); + fd_close(listener->fd); + listener->fd = -1; + --OpenFiles; + return -1; + } + + set_sock_opts(listener->fd, NULL, listener->socket_type); + + if (!unreal_bind(listener->fd, listener->file, 0, SOCKET_TYPE_UNIX)) + { + unreal_log(ULOG_FATAL, "listen", "LISTEN_BIND_ERROR", NULL, + "Could not listen on UNIX domain socket $file: $socket_error", + log_data_socket_error(listener->fd), + log_data_string("file", listener->file)); + fd_close(listener->fd); + listener->fd = -1; + --OpenFiles; + return -1; + } + + if (listen(listener->fd, LISTEN_SIZE) < 0) + { + unreal_log(ULOG_FATAL, "listen", "LISTEN_LISTEN_ERROR", NULL, + "Could not listen on UNIX domain socket $file: $socket_error", + log_data_socket_error(listener->fd), + log_data_string("file", listener->file)); + fd_close(listener->fd); + listener->fd = -1; + --OpenFiles; + return -1; + } + + fd_setselect(listener->fd, FD_SELECT_READ, listener_accept, listener); + + return 0; +} + +/** Create a listener port. + * @param listener The listen { } block configuration + * @returns 0 on success and <0 on error. Yeah, confusing. + */ +int unreal_listen(ConfigItem_listen *listener) +{ + if ((listener->socket_type == SOCKET_TYPE_IPV4) || (listener->socket_type == SOCKET_TYPE_IPV6)) + return unreal_listen_inet(listener); + return unreal_listen_unix(listener); +} + +/** Activate a listen { } block */ +int add_listener(ConfigItem_listen *listener) +{ + if (unreal_listen(listener)) + { + /* Error is already handled upstream */ + listener->fd = -2; + } + + if (listener->fd >= 0) + { + listener->options |= LISTENER_BOUND; return 1; } else { - conf->fd = -1; + listener->fd = -1; return -1; } } @@ -433,7 +544,7 @@ void consider_ident_lookup(Client *client) char buf[BUFSIZE]; /* If ident checking is disabled or it's an outgoing connect, then no ident check */ - if ((IDENT_CHECK == 0) || (client->server && IsHandshake(client))) + if ((IDENT_CHECK == 0) || (client->server && IsHandshake(client)) || IsUnixSocket(client)) { ClearIdentLookupSent(client); ClearIdentLookup(client); @@ -560,37 +671,38 @@ void set_socket_buffers(int fd, int rcvbuf, int sndbuf) } /** Set the appropriate socket options */ -void set_sock_opts(int fd, Client *client, int ipv6) +void set_sock_opts(int fd, Client *client, SocketType socket_type) { int opt; - if (ipv6) + if (socket_type == SOCKET_TYPE_IPV6) set_ipv6_opts(fd); -#ifdef SO_REUSEADDR - opt = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt)) < 0) + if ((socket_type == SOCKET_TYPE_IPV4) || (socket_type == SOCKET_TYPE_IPV6)) { - unreal_log(ULOG_WARNING, "socket", "SOCKET_ERROR_SETSOCKOPTS", client, - "Could not setsockopt(SO_REUSEADDR): $socket_error", - log_data_socket_error(-1)); - } +#ifdef SO_REUSEADDR + opt = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt)) < 0) + { + unreal_log(ULOG_WARNING, "socket", "SOCKET_ERROR_SETSOCKOPTS", client, + "Could not setsockopt(SO_REUSEADDR): $socket_error", + log_data_socket_error(-1)); + } #endif #if defined(SO_USELOOPBACK) && !defined(_WIN32) - opt = 1; - if (setsockopt(fd, SOL_SOCKET, SO_USELOOPBACK, (void *)&opt, sizeof(opt)) < 0) - { - unreal_log(ULOG_WARNING, "socket", "SOCKET_ERROR_SETSOCKOPTS", client, - "Could not setsockopt(SO_USELOOPBACK): $socket_error", - log_data_socket_error(-1)); - } + opt = 1; + if (setsockopt(fd, SOL_SOCKET, SO_USELOOPBACK, (void *)&opt, sizeof(opt)) < 0) + { + unreal_log(ULOG_WARNING, "socket", "SOCKET_ERROR_SETSOCKOPTS", client, + "Could not setsockopt(SO_USELOOPBACK): $socket_error", + log_data_socket_error(-1)); + } #endif - /* Previously we also called set_socket_buffers() to set some - * specific buffer limits. This is no longer needed on modern OS's. - * Setting it explicitly actually slows things down. - */ + } + + /* The following code applies to all socket types: IPv4, IPv6, UNIX domain sockets */ /* Set to non blocking: */ #if !defined(_WIN32) @@ -641,7 +753,7 @@ int is_loopback_ip(char *ip) for (e = conf_listen; e; e = e->next) { - if ((e->options & LISTENER_BOUND) && !strcmp(ip, e->ip)) + if ((e->options & LISTENER_BOUND) && e->ip && !strcmp(ip, e->ip)) return 1; } return 0; @@ -718,15 +830,15 @@ Client *add_connection(ConfigItem_listen *listener, int fd) Client *client; const char *ip; int port = 0; - + client = make_client(NULL, &me); + client->local->socket_type = listener->socket_type; - /* If listener is IPv6 then mark client (client) as IPv6 */ - if (listener->ipv6) - SetIPV6(client); + if (listener->socket_type == SOCKET_TYPE_UNIX) + ip = "127.0.0.1"; + else + ip = getpeerip(client, fd, &port); - ip = getpeerip(client, fd, &port); - if (!ip) { /* On Linux 2.4 and FreeBSD the socket may just have been disconnected @@ -762,29 +874,38 @@ refuse_client: SetLocalhost(client); } - /* Check set::max-unknown-connections-per-ip */ - if (check_too_many_unknown_connections(client)) + if (!(listener->options & LISTENER_CONTROL)) { - ircsnprintf(zlinebuf, sizeof(zlinebuf), - "ERROR :Closing Link: [%s] (Too many unknown connections from your IP)\r\n", - client->ip); - (void)send(fd, zlinebuf, strlen(zlinebuf), 0); - goto refuse_client; - } + /* Check set::max-unknown-connections-per-ip */ + if (check_too_many_unknown_connections(client)) + { + ircsnprintf(zlinebuf, sizeof(zlinebuf), + "ERROR :Closing Link: [%s] (Too many unknown connections from your IP)\r\n", + client->ip); + (void)send(fd, zlinebuf, strlen(zlinebuf), 0); + goto refuse_client; + } - /* Check (G)Z-Lines and set::anti-flood::connect-flood */ - if (check_banned(client, NO_EXIT_CLIENT)) - goto refuse_client; + /* Check (G)Z-Lines and set::anti-flood::connect-flood */ + if (check_banned(client, NO_EXIT_CLIENT)) + goto refuse_client; + } client->local->listener = listener; if (client->local->listener != NULL) client->local->listener->clients++; add_client_to_list(client); - irccounts.unknown++; - client->status = CLIENT_STATUS_UNKNOWN; - - list_add(&client->lclient_node, &unknown_list); + if (!(listener->options & LISTENER_CONTROL)) + { + /* IRC: unknown connection */ + irccounts.unknown++; + client->status = CLIENT_STATUS_UNKNOWN; + list_add(&client->lclient_node, &unknown_list); + } else { + client->status = CLIENT_STATUS_CONTROL; + list_add(&client->lclient_node, &control_list); + } if ((listener->options & LISTENER_TLS) && ctx_server) { @@ -809,7 +930,9 @@ refuse_client: goto refuse_client; } } - } + } else + if (listener->options & LISTENER_CONTROL) + start_of_control_client_handshake(client); else start_of_normal_client_handshake(client); return client; @@ -828,7 +951,7 @@ void start_of_normal_client_handshake(Client *client) RunHook(HOOKTYPE_HANDSHAKE, client); - if (!DONT_RESOLVE) + if (!DONT_RESOLVE && !IsUnixSocket(client)) { if (should_show_connect_info(client)) sendto_one(client, NULL, ":%s %s", me.name, REPORT_DO_DNS); @@ -1027,6 +1150,20 @@ void process_clients(void) } } } while(&client->lclient_node != &unknown_list); + + do { + list_for_each_entry(client, &control_list, lclient_node) + { + if ((client->local->fd >= 0) && DBufLength(&client->local->recvQ) && !IsDead(client)) + { + parse_client_queued(client); + if (IsDead(client)) + break; + } + } + } while(&client->lclient_node != &control_list); + + } /** Check if 'ip' is a valid IP address, and if so what type. @@ -1038,16 +1175,16 @@ void process_clients(void) int is_valid_ip(const char *ip) { char scratch[64]; - + if (BadPtr(ip)) return 0; if (inet_pton(AF_INET, ip, scratch) == 1) return 4; /* IPv4 */ - + if (inet_pton(AF_INET6, ip, scratch) == 1) return 6; /* IPv6 */ - + return 0; /* not an IP address */ } @@ -1060,11 +1197,21 @@ int ipv6_capable(void) int s = socket(AF_INET6, SOCK_STREAM, 0); if (s < 0) return 0; /* NO ipv6 */ - + CLOSE_SOCK(s); return 1; /* YES */ } +/** Return 1 if UNIX sockets of type SOCK_STREAM are supported, and 0 otherwise */ +int unix_sockets_capable(void) +{ + int fd = fd_socket(AF_UNIX, SOCK_STREAM, 0, "Testing UNIX socket"); + if (fd < 0) + return 0; + fd_close(fd); + return 1; +} + /** Attempt to deliver data to a client. * This function is only called from send_queued() and will deal * with sending to the TLS or plaintext connection. @@ -1156,7 +1303,7 @@ int deliver_it(Client *client, char *str, int len, int *want_read) int unreal_connect(int fd, const char *ip, int port, int ipv6) { int n; - + if (ipv6) { struct sockaddr_in6 server; @@ -1182,25 +1329,17 @@ int unreal_connect(int fd, const char *ip, int port, int ipv6) { return 0; /* FATAL ERROR */ } - + return 1; /* SUCCESS (probably still in progress) */ } /** Bind to an IP/port (port may be 0 for auto). * @returns 0 on failure, other on success. */ -int unreal_bind(int fd, const char *ip, int port, int ipv6) +int unreal_bind(int fd, const char *ip, int port, SocketType socket_type) { - if (ipv6) + if (socket_type == SOCKET_TYPE_IPV4) { - struct sockaddr_in6 server; - memset(&server, 0, sizeof(server)); - server.sin6_family = AF_INET6; - server.sin6_port = htons(port); - if (inet_pton(AF_INET6, ip, &server.sin6_addr.s6_addr) != 1) - return 0; - return !bind(fd, (struct sockaddr *)&server, sizeof(server)); - } else { struct sockaddr_in server; memset(&server, 0, sizeof(server)); server.sin_family = AF_INET; @@ -1209,4 +1348,43 @@ int unreal_bind(int fd, const char *ip, int port, int ipv6) return 0; return !bind(fd, (struct sockaddr *)&server, sizeof(server)); } + else if (socket_type == SOCKET_TYPE_IPV6) + { + struct sockaddr_in6 server; + memset(&server, 0, sizeof(server)); + server.sin6_family = AF_INET6; + server.sin6_port = htons(port); + if (inet_pton(AF_INET6, ip, &server.sin6_addr.s6_addr) != 1) + return 0; + return !bind(fd, (struct sockaddr *)&server, sizeof(server)); + } else + { + struct sockaddr_un server; + mode_t saved_umask; + int ret; + + unlink(ip); /* (ignore errors) */ + + memset(&server, 0, sizeof(server)); + server.sun_family = AF_UNIX; + strlcpy(server.sun_path, ip, sizeof(server.sun_path)); + saved_umask = umask(077); // TODO: make this configurable + ret = !bind(fd, (struct sockaddr *)&server, sizeof(server)); + umask(saved_umask); + + return ret; + } } + +#ifdef _WIN32 +void init_winsock(void) +{ + WSADATA WSAData; + if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0) + { + MessageBox(NULL, "Unable to initialize WinSock", "UnrealIRCD Initalization Error", MB_OK); + fprintf(stderr, "Unable to initialize WinSock\n"); + exit(1); + } +} +#endif diff --git a/src/tls.c b/src/tls.c index c84ef02..b7b2429 100644 --- a/src/tls.c +++ b/src/tls.c @@ -512,7 +512,7 @@ int init_tls(void) /** Reinitialize TLS server and client contexts - after REHASH -tls */ -void reinit_tls(void) +int reinit_tls(void) { SSL_CTX *tmp; ConfigItem_listen *listen; @@ -524,7 +524,7 @@ void reinit_tls(void) { unreal_log(ULOG_ERROR, "config", "TLS_RELOAD_FAILED", NULL, "TLS Reload failed. See previous errors."); - return; + return 0; } if (ctx_server) SSL_CTX_free(ctx_server); @@ -535,7 +535,7 @@ void reinit_tls(void) { unreal_log(ULOG_ERROR, "config", "TLS_RELOAD_FAILED", NULL, "TLS Reload failed at client context. See previous errors."); - return; + return 0; } if (ctx_client) SSL_CTX_free(ctx_client); @@ -551,7 +551,7 @@ void reinit_tls(void) { unreal_log(ULOG_ERROR, "config", "TLS_RELOAD_FAILED", NULL, "TLS Reload failed at listen::tls-options. See previous errors."); - return; + return 0; } if (listen->ssl_ctx) SSL_CTX_free(listen->ssl_ctx); @@ -569,7 +569,7 @@ void reinit_tls(void) { unreal_log(ULOG_ERROR, "config", "TLS_RELOAD_FAILED", NULL, "TLS Reload failed at sni::tls-options. See previous errors."); - return; + return 0; } if (sni->ssl_ctx) SSL_CTX_free(sni->ssl_ctx); @@ -588,13 +588,15 @@ void reinit_tls(void) unreal_log(ULOG_ERROR, "config", "TLS_RELOAD_FAILED", NULL, "TLS Reload failed at link $servername due to outgoing::tls-options. See previous errors.", log_data_string("servername", link->servername)); - return; + return 0; } if (link->ssl_ctx) SSL_CTX_free(link->ssl_ctx); link->ssl_ctx = tmp; /* activate */ } } + + return 1; } /** Set SSL connection as nonblocking */ diff --git a/src/unrealircdctl.c b/src/unrealircdctl.c new file mode 100644 index 0000000..ddb094e --- /dev/null +++ b/src/unrealircdctl.c @@ -0,0 +1,266 @@ +/************************************************************************ + * UnrealIRCd - Unreal Internet Relay Chat Daemon - src/unrealircdctl + * (c) 2022- Bram Matthys and 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. + */ + +/** @file + * @brief UnrealIRCd Control + */ +#include "unrealircd.h" + +#ifdef _WIN32 + #define UNREALCMD "unrealircdctl" +#else + #define UNREALCMD "./unrealircd" +#endif + + +extern int procio_client(const char *command, int auto_color_logs); + +void unrealircdctl_usage(const char *program_name) +{ + printf("Usage: %s