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